/**
 * @module NGS.plugins
 * @description NGS 系统插件
 * @author Lori
 */
"use strict";
/**
 * @class Chat
 * @description 提供坐席聊天功能
 */
define(['js/lib/async.min', 'js/plugins/chat/chat.common', 'js/plugins/chat/i18n/chat.' + $E.lang, 'js/plugins/chat/jquery.rateyo.min'], function (async, $COMM, i18n) {
    /**
     * @class Chat
     * @constructor
     * @param {object} project 项目对象
     * @param {dom} container 插件装载的容器
     */
    var chatServerAddr,
        chatServerHost,
        chatServerName,
        _messageId = 0,
        sendingTimer = 0,
        EMOTION = $COMM.EMOTION,
        ICONS = $COMM.ICONS,
        REQUEST_STATUS = $COMM.REQUEST_STATUS,
        CHAT_REQUEST_TYPE = $COMM.CHAT_REQUEST_TYPE,
        USER_STATUS = $COMM.USER_STATUS,
        CLIENT_TYPE = $COMM.CLIENT_TYPE,
        NOTICE_TYPE = $COMM.NOTICE_TYPE,
        MESSAGE_TYPE = $COMM.MESSAGE_TYPE,
        Tools = $COMM.Tools,
        SoundManager = $COMM.SoundManager,
        Robot = $COMM.Robot,
        acceptRoomId = '',
        acceptVideoSessionId = '',
        globalMeeting,
        isVideoCallIng,
        videoNoResponseTimer,
        videoNoResponseTime = 15000,
        meetingStartTime,
        videoSdkType,
        currentVideoSessionId,
        videoActiveIng = false

    /**
     * 用户对象
     * @param id
     * @param firstName
     * @param lastName
     * @param alias
     * @param headImg
     * @constructor
     */
    function User(id, firstName, lastName, alias, headImg) {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
        this.alias = alias;
        this.headImg = headImg;
    }

    /**
     * 会话对象
     * @param id
     * @constructor
     */
    function Session(id) {
        this.id = id;
        this.clientId = -1;
        this.client = {};
        this.preMsgUserId = "";
        this.previewMessage = "";
        this.allMessages = [];
        this.active = true; //boolean: if the session is still active
        this.creationTime = new Date().getTime();
        this.endTime;
        this.timeLapse = 0;
        this.hasPushedRating = false;
        this.serviceAccountId = "";
        this.serviceAccountName = "";
        this.lastUserMessageReceivedTime = -1;
        this.lastAutoReplyMessageTime = -1;
        this.lastAgentMessageSentTime = -1;
        this.userInWaiting = false;
        this.autoReplyMessageTimes = 0;
        this.queueId = -1;
        this.queueName = "";
        this.users = {};
        this.userCount = 0;
        this.hasCloseAFK = false;
        this.customNotice;
        this.quoteMessage = {};
        this.permFlag = 0;
        this.pushRatingCount = 0;
        this.firstReply = false;
        this.languageTo = '';
        this.videoActive = false;
        this.lastMinimaxMessage = ''; // minimax最后一次被调用时的消息
        this.stdQuestions = [];
    }

    /**
     * 消息对象
     * @param id
     * @param userId
     * @param userName
     * @param text
     * @param formattedTime
     * @param icon
     * @param revokeFlag
     * @param type
     * @constructor
     */
    function Message(id, userId, userName, text, translate, stateId, formattedTime, icon, revokeFlag, type, read, readMessageId, monitorSender = '', quoteMessage = undefined) {
        this.id = id;
        this.userId = userId;
        this.userName = userName;
        this.text = text;
        this.translate = translate;
        this.stateId = stateId;
        this.formattedTime = formattedTime;
        this.icon = icon;
        this.revokeFlag = revokeFlag || 0;
        this.type = type || 0; // 0普通消息, 1系统消息, 2表示系统自动发送的消息
        this.read = read;
        this.newMessageId = readMessageId;
        this.readMessageId = readMessageId;
        this.monitorSender = monitorSender;
        this.quoteMessage = quoteMessage;
    }

    /**
     *  #########  聊天插件主类  #########
     * @param project
     * @param container
     * @param parameters
     * @returns {Chat}
     * @constructor
     */
    var Chat = function (project, container, parameters) {
        var c = this,
            // 会话pull错误次数
            pullErrorCount = 0,
            //最后一次pull时间
            lastPullTime,
            //所有会话id数组
            sessionIdsArr = [],
            //所有会话数组
            sessionsMap = {},
            //所有请求id数组
            requestIdsArr = [],
            //当前请求id
            currentRequestId,
            //当前会话id
            currentSessionId,
            lastCreatedSessionId,
            currentCustomerGuid,
            currentTIR,
            allowCloseSessionDirectly = true,
            sendType = 0,
            chatStyle,
            headImgBaseUrl = location.pathname.substring(0, location.pathname.indexOf('/', 1)) + '/fs/get?file=',
            soundManager,
            tools,
            enableClientRating = false,
            currentVoiceAudio,
            showSessionButtons,
            showTypeOnName = false,
            chatExclusion = false,
            sinkAfterEnds = false,
            requestTabs,
            lastChattingTimeThreshold,
            //请求进度条计时器, 多个请求，以请求id为key
            requestProgressTimer = {},
            //定时更新用户等待时长计时器
            userWaitingTimer,
            //用户状态超时定时器
            userStatusTimer,
            //输入提示显隐的计时器
            typingTimer,
            //输入提示增强效果计时器
            typingEffectTimer,
            //是否启用建议搜索
            suggestionSearchEnabled = false,
            //个性配置中是否开启建议搜索
            customSuggestionEnabled = true,
            //超过多少个字符后，就不去给出建议
            suggestionMaxWordLength = 10,
            //建议检索计时器，避免短时间内发出大量请求
            suggestionSearchTimer,
            //上一次查找建议时候搜的内容
            lastSearchContentForSuggestion,
            //每次获取历史消息的条数
            countForFetchingPreviousMessage = 30,
            //客户相关缓存信息
            clientCache = {},
            //持久会话列表更新计时器
            updatePermSessionsTimer,
            timeoutForUpdatePermSessions = 900 * 1000,
            topSessionOnNewMessage = 1,
            //是否卡其脏词扫描
            dirtyWordsScanEnabled = false,
            // 是否开启关键词扫描
            highlightWordsScanEnable = false,
            // MINIMAX智能辅助总结参数
            summaryParams = {},
            // MINIMAX智能辅助优化参数
            optimizationParams = {},
            aiResponseParams = {},
            optimizationArray = [],
            summaryKey = '',
            summaryOption = 0,
            //是否允许消息撤回
            messageRevokeEnabled = false,
            messageQuoteEnabled = false,
            checkPullTimer,
            // 是否开启辅助机器人自动应答
            robotAutoAnswerEnabled = false,
            //是否开启新消息声音提醒
            newMassageSoundEnabled = false,
            // 是否开启发送消息的同时自动翻译内容
            translateAutoEnabled = false,
            // 是否开启不用提示直接推送满意度
            pushRatingDirectly = false,
            // 是否允许跨工作组转接
            enableCrossWorkgroupTransfer = false,
            // 是否启用splitter
            disableSplitter = false,
            // 发送typingNotice的最小间隔时间（秒）
            typingNotificationDelay = 10,
            // 上一次发送typingNotice的时间
            lastTypingNotificationTime = 0,
            // 消息展示样式类型
            messageBubbleStyle = undefined,
            // 网络环境不佳时出现页面锁定的请求错误次数
            chatErrorCount = 3,
            chatListTimeSytles = [],
            // 新消息来时候是否通知框架
            newMessageNotifyEnabled = false,
            // 发送消息最长限制
            maxSendingWordLength = 0,
            // 系统自动发送通知消息是否需要提醒，默认不提醒
            systemNoticeNotify = false,
            //用来限制客户发送了消息，坐席没有回复之后，自动发送消息内容的展示次数 0 表示没有限制， 大于 0 的表示限制的消息发送次数
            sysAutoReplyMessageTimes = 0,
            // 是否禁止上传
            uploadDisabled = false,
            // 是否展示坐席辅助小浮屏
            showAgentAssistIframe = undefined,
            // 粘贴图片或者截图后，图标是弹出是否发送还是进入输入框
            editImageAfterPaste = false,
            //含有敏感词的内容是否禁止发送
            chatSensitiveSend = 0


        this.project = project;
        Robot.init(project);
        this.container = container;
        this.staff;
        this.configs;
        this.logined = false;
        this.currentUserStatus = USER_STATUS.OFFLINE;
        this.queues = [];
        // 选中队列下所有非离线坐席列表
        this.agentList = [];
        this.worker = new Worker($E.url + '/js/plugins/chat/chat.worker.js?v=' + $E.ver);
        // 消息是否正在发送中
        this.sending = false;
        this.userStatus = {};
        this.parameters = parameters || {};
        //chat服务地址
        // chatServerAddr = 'https://idemo.elitecrm.com/webchat';
        chatServerAddr = project.getParam('CHATAD');
        // chatServerAddr = 'http://' + location.hostname + ':' + location.port + '/webchat'
        // chatServerAddr = 'https://localhost:448/webchat'
        //是否允许直接关闭会话，如果不允许则会发出关闭消息
        allowCloseSessionDirectly = project.getParam('CTACSD') == 0 ? false : true;
        //是否允许客户评级
        enableClientRating = getIntParam(project, 'CACREN') === 1;
        //是否显示转接 邀请 转接组 这些按钮到聊天界面右上角
        showSessionButtons = project.getParam('CHASSB');
        if (showSessionButtons) {
            showSessionButtons = JSON.parse(showSessionButtons);
        }
        //CHTSTY 表示 chat插件使用的样式
        chatStyle = project.getParam('CHTSTY') || 'chat';
        //是否在名字上显示客户来源类型
        showTypeOnName = getIntParam(project, 'CHATON') === 1;
        //是否与软电话互斥
        chatExclusion = getIntParam(project, 'CHAECS') === 1;
        //结束的会话是不是要下沉到tab的底部
        sinkAfterEnds = getIntParam(project, 'CHASAE') === 1;
        //客户发送消息作息未回复，自动推送消息的次数限制
        sysAutoReplyMessageTimes = getIntParam(project, 'CHARMT') || 0;
        //上一个消息收到到现在多长时间，需要给出警示颜色（单位秒）
        lastChattingTimeThreshold = project.getParam('CHALTT');
        //是否允许坐席消息撤回，允许的话，新发消息右击可以实现消息撤回操作
        messageRevokeEnabled = getIntParam(project, 'CHAMRV') === 1;
        //是否允许坐席消息引用
        messageQuoteEnabled = getIntParam(project, 'CHAQOT') === 1;
        // 是否允许跨工作组发起转接会议请求
        enableCrossWorkgroupTransfer = getIntParam(project, 'CHACWT') === 1;
        // 是否关闭splitter
        disableSplitter = getIntParam(project, 'CHADSP') === 1;
        // 消息气泡展示类型
        messageBubbleStyle = project.getParam('CHAMBS');
        // 收到新消息后是否发出事件通知框架
        newMessageNotifyEnabled = getIntParam(project, 'CHANNM') === 1;
        // 网络环境不佳时出现页面锁定的请求错误次数
        chatErrorCount = Number(project.getParam('CHAPEC')) || 3;
        // 超时聊天列表变色
        chatListTimeSytles = Boolean(project.getParam('CHALTS') && project.getParam('CHALTS') !== '') ? sortByTime(JSON.parse(project.getParam('CHALTS'))) : [];
        //禁用词消息是否禁止发送,为true则禁止发送
        chatSensitiveSend = getIntParam(project, 'CHASWD') === 1
        //是否启用建议搜索功能
        let CHASSE = project.getParam('CHASSE');
        if (CHASSE) {
            try {
                CHASSE = JSON.parse(CHASSE);
                suggestionSearchEnabled = CHASSE.enabled;
                if (!isNaN(CHASSE.maxWordLength)) {
                    suggestionMaxWordLength = CHASSE.maxWordLength;
                }
            } catch (e) { }
        }
        // 默认表情是否需要隐藏： 1表是隐藏qq表情，2表示隐藏emoji表情， 3表示都隐藏, 4表示qq表情和emoji表情位置互换
        let CHAEMO = $F.parseInt(project.getParam('CHAEMO'));
        // chat中ajax请求最大重试次数，默认3次
        let CHAMRT = project.getParam('CHAMRT');
        // 包装url的逻辑，是不是需要包装，是否符合黑白名单
        let CHAWUR = project.getParam('CHAWUR');
        if (CHAWUR) {
            try {
                CHAWUR = JSON.parse(CHAWUR);
            } catch (e) { }
        }
        // 是否开启高亮词扫描
        highlightWordsScanEnable = getIntParam(project, 'CHAHWS') === 1;
        // 最大发送消息长度，默认未0表示不限制
        maxSendingWordLength = project.getParam('CHAMSL') || 0;
        try {
            const CHAIAB = JSON.parse(project.getParam('CHAIAB')) || {}
            // MINIMAX总结参数
            summaryParams = CHAIAB.summaryParams || {};
            // MINIMAX优化参数
            optimizationParams = CHAIAB.optimizationParams || {};
            // MINIMAX智能回复参数
            aiResponseParams = CHAIAB.aiResponseParams || {};
        } catch (e) { }
        // 系统通知是否需要提醒
        systemNoticeNotify = getIntParam(project, 'CHASNN') === 1;
        // 是否屏蔽上传
        uploadDisabled = getIntParam(project, 'CHAAUD') === 1;
        //是否展示坐席辅助的小浮屏的iframe {url： "", iconUrl: "", style: {}}

        var showAgentAssistIframeStr = project.getParam('CHAIFM');
        if (showAgentAssistIframeStr) {
            try {
                let showAgentAssistIframeObj = JSON.parse(showAgentAssistIframeStr);
                showAgentAssistIframe = {
                    id: 'agentAssistWindow',
                    url: showAgentAssistIframeObj.url,
                    style: showAgentAssistIframeObj.style,
                    imageUrl: showAgentAssistIframeObj.imageUrl
                };
            } catch (e) {
                $F.log("Chat", "Parse CHAIFM error: " + showAgentAssistIframeStr);
            }
        }
        editImageAfterPaste = getIntParam(project, 'CHAIAP') === 1;

        //测试环境中需要设置的一些配置
        if (location.hostname === "127.0.0.1" && (location.port === "8098" || location.port === "8080" || location.port === "8088") || location.hostname === "192.168.2.80" && location.port === "8080") {
            // chatServerAddr = 'http://' + location.hostname + ':' + (location.port === "8099" ? '8099' : '8980') + '/webchat';
            // chatServerAddr = 'http://idemo.elitecrm.com/ngs/webchat'
            chatServerAddr = 'http://' + location.hostname + ':8080/webchat';
            showSessionButtons = 1;
            enableClientRating = false;
            chatStyle = 'chat-white';
            //chatExclusion = true;
            //project.params['CHAECS'] = '1';
            messageRevokeEnabled = true;
            lastChattingTimeThreshold = 10;
            sinkAfterEnds = true;
            suggestionSearchEnabled = true;
            //            require(['js/elasticsearch.client' + $E.jsv], function(esc){
            //    			esc.init({"hosts":"127.0.0.1:9200","log":"trace"}, function(){
            //
            //    			}, function(error){
            //    				console.error(error);
            //    			});
            //    			project.esc = esc;
            //            });
            CHAEMO = 4;
            enableCrossWorkgroupTransfer = true;
            messageBubbleStyle = 'dc';
            highlightWordsScanEnable = true;
            CHAWUR = {
                nowrap: false,
                whiteList: [],
                blackList: ["www.elitecrm.com"]
            };
            editImageAfterPaste = true;
        }
        if (!chatServerAddr) {
            $F.alert(i18n.error.chatServerCheckAlert);
            return;
        }
        chatServerHost = chatServerAddr.substring(0, chatServerAddr.lastIndexOf("/"));
        chatServerName = chatServerAddr.substring(chatServerAddr.lastIndexOf("/"), chatServerAddr.length);
        soundManager = new SoundManager(chatServerAddr);
        tools = new Tools(chatServerAddr, CHAMRT);

        function sortByTime(arr) {
            arr.sort(function (a, b) {
                return a.time - b.time;
            });
            return arr;
        }

        //调用服务接口
        var callService = function (params, retry) {
            if (params && params.url) {
                params.url = tools.chatServerAddr + "/" + params.url;
            }
            return tools.callService(params, (retry ? 0 : undefined));
        };

        var getPropertyByQueue = function (property, queueId, lang, defaultValue) {
            return new Promise(resolve => {
                let key = queueId;
                if (lang) {
                    key = queueId + lang;
                }
                if (c.configs.queueConfigs[key]) {
                    resolve(c.configs.queueConfigs[key][property] || defaultValue);
                } else { //没有此队列相关属性，重新获取
                    tools.callService({
                        data: {
                            requestType: "fetchQueueConfig",
                            queueId: queueId,
                            lang: lang
                        }
                    }).then(data => {
                        if (data.status === 'success') {
                            c.configs.queueConfigs[key] = data.config;
                            resolve(c.configs.queueConfigs[key][property] || defaultValue);
                        } else {
                            resolve(defaultValue);
                        }
                    }).fail(() => {
                        resolve(defaultValue);
                    });
                }
            });
        };

        //聊天插件是否隐藏
        function chatPanelIsHidden() {
            return $('.chat', container).is(":hidden") || ($('.chat', container).width() === 0);
        };

        function clearSessionRequest(clearSessionId, clearRoomId) {
            const sentInfoJSON = JSON.stringify({
                sessionId: clearSessionId,
                roomId: clearRoomId,
                actionType: "clearSession",
                from: 2,
            });
            callService({
                data: {
                    requestType: "videoMeetingEvent",
                    data: sentInfoJSON
                },
                success: data => { }
            }, true).fail(function (e) {
                $F.alert(i18n.error.requestFailed + ',clearSessionRequest:' + error);
            });
        };

        function unAnsweredVedioRequest(unAnsweredSessionId, unAnsweredRoomId) {
            const sentInfoJSON = JSON.stringify({
                sessionId: unAnsweredSessionId,
                roomId: unAnsweredRoomId,
                actionType: "noResponse",
                from: 2,
            });
            callService({
                data: {
                    requestType: "videoMeetingEvent",
                    data: sentInfoJSON
                },
                success: data => {
                    sendMessage(`${i18n.videoMeetingRecord.Unanswer} [videoMeet]`, unAnsweredSessionId)
                }
            }, true).fail(function (e) {
                $F.alert(i18n.error.requestFailed + ',clearSessionRequest:' + error);
            });
        };

        function formatTimestampDiff(diff) {
            // 计算小时、分钟和秒数
            const hours = Math.floor(diff / (1000 * 60 * 60));
            const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
            const seconds = Math.floor((diff % (1000 * 60)) / 1000);

            // 格式化输出
            const formattedDiff = `${hours < 10 ? '0' + hours : hours}:${minutes < 10 ? '0' + minutes : minutes}:${seconds < 10 ? '0' + seconds : seconds}`;

            return formattedDiff;
        };

        function pullFromServer(c) {
            var sessionIds = [],
                sessionPerms = [];
            $.each(sessionsMap, function (key, sessionCur) {
                if (sessionCur.active && sessionCur.id > 0) {
                    sessionIds.push(sessionCur.id);
                    sessionPerms.push(sessionCur.permFlag);
                }
            });
            var pullSendInfoJSON = JSON.stringify({
                "senderId": c.configs.agentId,
                "chatRequestIdsFromUser": requestIdsArr,
                sessionIds: sessionIds,
                sessionPerms: sessionPerms
            });

            lastPullTime = new Date().getTime();
            const cloakArray = document.querySelectorAll('.chat_cloak')
            $F.log('Chat - pull', 'Start pull: ' + pullSendInfoJSON);
            try {
                callService({
                    data: {
                        requestType: "pull",
                        data: pullSendInfoJSON
                    }
                }).then(function (data) {
                    pullErrorCount = 0
                    if (cloakArray && cloakArray.length && cloakArray[0].style && cloakArray[0].style.display !== 'none') {
                        $('.chat_cloak').hide()
                    }
                    if (data === 'retry') { } else if (data === 'NOT_LOGGED_IN') {
                        $F.alert(i18n.error.notLoggedInAlert);
                        c.closeChat();
                    } else {
                        processJSONPullReturn(data);
                    }
                }).fail(function () {
                    pullErrorCount = pullErrorCount + 1;
                    if (pullErrorCount > chatErrorCount) {
                        if (cloakArray && cloakArray.length && cloakArray[0].style && cloakArray[0].style.display === 'none') {
                            cloakArray.forEach((cloak) => {
                                cloak.style.display = 'grid'
                            })
                            // const chatPanel = document.querySelector('#chatPanel')
                            // const cloak = document.createElement('div')
                            // cloak.className = 'chatCloak'
                            // cloak.innerHTML = '<div class="chatClockTip"><i class="fa fa-spinner"></i><span> '+ i18n.error.networkReconnect +'</span></div>'
                            // if (chatPanel) {
                            //     chatPanel.append(cloak)
                            // }
                        }
                    }
                })
            } finally {
                c.worker.postMessage({
                    cmd: 'doPull',
                    params: {
                        timeout: c.configs.timeoutForPull
                    }
                });
            }
        }

        /**
         * 指定当前session里的客户等待状态，并且在设置状态的时候设置发送消息的次数，或者是重置发送消息次数
         * @param session
         */
        function setUserInWaiting(session, userInWaiting) {
            session.userInWaiting = userInWaiting;
            //如果是设置当前客户状态,就重置发送的次数为0
            session.autoReplyMessageTimes = 0;
        }

        function processJSONPullReturn(jsonResponse) {
            console.log('Chat - processJSONPullReturn', JSON.stringify(jsonResponse));
            if (jsonResponse.status === 'success') {
                let receivedInfo = jsonResponse.pullResponse;
                //process chat request data
                let chatRequestToUser = receivedInfo.chatRequestToUser;
                if (chatRequestToUser) {
                    let requestId = chatRequestToUser.id,
                        requestType = chatRequestToUser.requestType,
                        user = chatRequestToUser.user;

                    $F.log("Chat - processJSONPullReturn", "Receive new chat request " + requestId + " from " + user.id);
                    newMassageSoundEnabled && soundManager.play('newRequestSound');
                    currentRequestId = requestId;

                    if (requestType == CHAT_REQUEST_TYPE['agentInvite'] ||
                        requestType == CHAT_REQUEST_TYPE['agentTransfer'] ||
                        requestType == CHAT_REQUEST_TYPE['agentTransferToQueue']) {
                        //如果是转接或者会议的，只有此参数配置为2时候，才会自动接受
                        if (c.configs.defaultDirectAccept == 2) {
                            // 发送接收请求
                            sendRequestResponse(requestId, 'yes');
                        } else {
                            if (Robot.isRobot(user.id) && c.configs.defaultDirectAccept) { //如果是机器人发出的转接，则直接接受
                                sendRequestResponse(requestId, 'yes');
                            } else {
                                // 弹出请求确认框
                                showRequestDialog(requestId, user, chatRequestToUser);
                            }
                        }
                    } else {
                        if (c.configs.defaultDirectAccept) {
                            // 发送接收请求
                            sendRequestResponse(requestId, 'yes');
                        } else {
                            // 弹出请求确认框
                            showRequestDialog(requestId, user, chatRequestToUser);
                        }
                    }
                }

                //process request
                var chatRequestsFromUser = receivedInfo.chatRequestsFromUser;
                if (chatRequestsFromUser) {
                    for (let chatRequestIndex = 0; chatRequestIndex < chatRequestsFromUser.length; chatRequestIndex++) {
                        let chatRequest = chatRequestsFromUser[chatRequestIndex];
                        let id = chatRequest.id;
                        Tools.removeItemFromArr(requestIdsArr, id);
                        let status = chatRequest.status;
                        let requestType = chatRequest.requestType;
                        let sessionId = chatRequest.sessionId;
                        let feedbackMessage = chatRequest.feedbackMessage;
                        if (feedbackMessage) {
                            addMessageToSession(sessionId, {
                                id: "-1",
                                name: null
                            }, {
                                text: formatFeedbackMessage(feedbackMessage),
                                type: MESSAGE_TYPE.SYSTEM
                            });
                            if (sessionId == currentSessionId) {
                                appendTextToDiv(formatFeedbackMessage(feedbackMessage));
                            }
                        }
                        if (status == REQUEST_STATUS['accepted']) {
                            var session = sessionsMap["session" + sessionId];
                            if (requestType == CHAT_REQUEST_TYPE['agentTransfer'] ||
                                requestType == CHAT_REQUEST_TYPE['agentTransferToQueue']) { //agent transfer
                                $F.log("Chat - processJSONPullReturn", "transfer session " + sessionId + " accepted");
                                if (c.configs.closeTabWhenTransferAccepted) {
                                    removeTabAndSession(sessionId);
                                } else {
                                    session.active = false;
                                    session.endTime = new Date().getTime();
                                    disableSession(sessionId);
                                    sinkSession(sessionId);
                                    refreshSessionStat();
                                }
                            } else if (requestType == CHAT_REQUEST_TYPE['agentInvite']) {
                                var user = chatRequest.user;
                                session.users[user.id] = new User(user.id, user.firstName, user.lastName, user.alias, user.headImg);
                                session.userCount++;
                                $('.session_hd .title_name', container).html(session.client.name + '(' + session.userCount + ')');
                            }
                        }
                    }
                }

                //process session data
                let sessions = receivedInfo.sessions;
                if (sessions && sessions.length > 0) {
                    for (let sessionIndex = 0; sessionIndex < sessions.length; sessionIndex++) {
                        let session = sessions[sessionIndex],
                            sessionId = session.id;
                        let thisSession = sessionsMap["session" + sessionId];
                        if (!thisSession) {
                            //会话转接时候，如果直接关闭了转接成功的会话，这里会获取不到这个thisSession对象，所以加了保护
                            if (!session.session) continue;
                            // 如果有这个session.session 表示要新建这个会话
                            let queueId = session.session.queueId,
                                queueName = session.session.queueName,
                                sessionParams = session.session.sessionParams,
                                client = session.session.client;
                            // 如果本地没有此会话，可能需要新建这个会话item
                            let newSession = createSession(sessionId, client, queueId, queueName, sessionParams);
                            newSession.permFlag = session.session.permFlag;
                            sessionsMap["session" + sessionId] = newSession;
                            sessionIdsArr[sessionIdsArr.length] = sessionId;
                            lastCreatedSessionId = "session" + sessionId;
                            setupClientDisplayName(client);
                            setupClientIcon(client);
                            createNewTab('sessionTab_' + sessionId, client);
                            thisSession = newSession;
                            if (!currentSessionId) {
                                switchTabAndSession('sessionTab_' + sessionId);
                            }
                        }
                        let sessionNotices = session.sessionNotices;
                        if (sessionNotices) {
                            for (let noticeIndex = 0; noticeIndex < sessionNotices.length; noticeIndex++) {
                                let notice = sessionNotices[noticeIndex];
                                let noticeMessage = notice.message;
                                let noticeType = notice.type;
                                //除了typing和custom 其他notice消息都要添加的会话消息中
                                if (noticeType != NOTICE_TYPE.TYPING &&
                                    noticeType != NOTICE_TYPE.CUSTOM &&
                                    noticeType != NOTICE_TYPE.MESSAGE_STATE_SEND &&
                                    noticeType != NOTICE_TYPE.VOICE_TRANSFER_OVER_NOTICE &&
                                    noticeType != NOTICE_TYPE.MESSAGE_STATE_ACCEPTED &&
                                    noticeType != NOTICE_TYPE.MESSAGE_STATE_REJECTED &&
                                    noticeType != NOTICE_TYPE.MESSAGE_STATE_DELIVERED &&
                                    noticeType != NOTICE_TYPE.MESSAGE_STATE_FAILED &&
                                    noticeType != NOTICE_TYPE.MESSAGE_STATE_READ &&
                                    noticeType != NOTICE_TYPE.REVOKE_MESSAGE
                                ) {
                                    addMessageToSession(sessionId, {
                                        id: "-1",
                                        name: null
                                    }, {
                                        text: formatFeedbackMessage(noticeMessage),
                                        type: MESSAGE_TYPE.SYSTEM
                                    });
                                }
                                if (noticeType == NOTICE_TYPE.TYPING) { // 输入提示
                                    if (sessionId == currentSessionId) {
                                        noticeMessage = noticeMessage.split('_')[1];
                                        setTypingNotice(noticeMessage);
                                    }
                                } else if (noticeType == NOTICE_TYPE.TRACK_CHANGE) { // 轨迹变化提示

                                } else if (noticeType === NOTICE_TYPE.REVOKE_MESSAGE) { // 消息撤回
                                    thisSession.allMessages.forEach(function (message) {
                                        if (message.id == noticeMessage) {
                                            message.revokeFlag = 1
                                            const revokedElement = document.createElement('div');
                                            revokedElement.innerHTML = message.displayMessage;
                                            const bubbleElement = revokedElement.querySelector('.js_message_bubble');
                                            const revokedTipElement = document.createElement('div');
                                            revokedTipElement.textContent = i18n.revoked;
                                            revokedTipElement.className = 'revoked'
                                            bubbleElement.insertAdjacentElement('afterend', revokedTipElement);
                                            message.displayMessage = revokedElement.innerHTML;
                                            document.querySelector('[data-id="' + noticeMessage + '"]').closest('.js_message_bubble').insertAdjacentHTML('afterend', '<div class="revoked">' + i18n.revoked + '</div>');
                                        }
                                    })
                                    console.log('Revoke message: ' + noticeMessage)
                                } else if (noticeType === NOTICE_TYPE.MESSAGE_STATE_FAILED) {

                                } else if (noticeType === NOTICE_TYPE.VOICE_TRANSFER_OVER_NOTICE) {
                                    const messageId = noticeMessage.messageId;
                                    const recognition = noticeMessage.recognition;
                                    if (sessionId == currentSessionId) {
                                        $('.js_message_plain[data-id=' + messageId + ']', container).html(recognition);
                                        wrapMessage($('.js_message_plain[data-id=' + messageId + ']', container));
                                    }

                                    let thisSession = sessionsMap["session" + sessionId];
                                    thisSession.allMessages.forEach(function (message) {
                                        if (message.id == messageId) {
                                            message.text = recognition;
                                            try {
                                                message.translate = $(recognition).attr('data-recognition') || '';
                                            } catch (e) { }
                                            message.displayMessage = getDisplayMessage(message, session);
                                        }
                                    })
                                } else if (noticeType === NOTICE_TYPE.MESSAGE_STATE_READ ||
                                    noticeType === NOTICE_TYPE.MESSAGE_STATE_SEND ||
                                    noticeType === NOTICE_TYPE.MESSAGE_STATE_ACCEPTED ||
                                    noticeType === NOTICE_TYPE.MESSAGE_STATE_REJECTED ||
                                    noticeType === NOTICE_TYPE.MESSAGE_STATE_DELIVERED) {
                                    console.log('Message Read: ' + noticeMessage)
                                    var messageStateTxt = '';
                                    let thisSession = sessionsMap["session" + sessionId];
                                    thisSession.allMessages.forEach(function (message) {
                                        if (message.read) {
                                            if (noticeMessage) {
                                                if (noticeMessage.indexOf(message.readMessageId + "") > -1) {
                                                    message.read = i18n.message.hasRead;
                                                    message.displayMessage = '';
                                                }
                                            } else {
                                                message.read = i18n.message.hasRead;
                                                message.displayMessage = '';
                                            }
                                        }
                                    });
                                    if (sessionId == currentSessionId)
                                        if (noticeMessage) {
                                            $.each(noticeMessage.split(','), function (i, notice) {
                                                $('.has-read-' + notice, container).text(i18n.message.hasRead);
                                            })
                                        } else {
                                            $('.has-read', container).text(i18n.message.hasRead);
                                        }
                                } else if (noticeType == NOTICE_TYPE.TAKE_OVER_NOTICE) {
                                    let extra = notice.extra;
                                    if (extra) {
                                        extra = JSON.parse(extra);
                                        createNewTab('sessionTab_' + extra.id, extra.client);
                                    }
                                } else if (noticeType == NOTICE_TYPE.CUSTOM) { // 自定义notice
                                    thisSession.customNotice = noticeMessage; //保存到session的属性上
                                    addMessageToSession(sessionId, {
                                        id: "-1",
                                        name: null
                                    }, {
                                        text: formatFeedbackMessage(noticeMessage),
                                        type: MESSAGE_TYPE.SYSTEM
                                    });
                                    if (sessionId == currentSessionId)
                                        appendTextToDiv(formatFeedbackMessage(noticeMessage))
                                    $F.log('Chat - processJSONPullReturn', 'Fire CUSTOM_NOTICE to session[' + sessionId + ']: ' + noticeMessage);
                                    notifyProjectEvent($CONST.ChatEvent.CUSTOM_NOTICE, {
                                        currentSessionId: currentSessionId,
                                        sessionId: sessionId,
                                        message: noticeMessage
                                    });
                                } else {
                                    if (noticeType == NOTICE_TYPE.USER_LEFT_NOTICE) {
                                        thisSession.userCount--;
                                        if (sessionId == currentSessionId) {
                                            $('.session_hd .title_name', container).html(thisSession.client.name + ((thisSession.userCount > 2) ? '(' + thisSession.userCount + ')' : ''));
                                        }
                                        var currentVideoSession = sessionsMap["session" + currentVideoSessionId];
                                        if (sessionId == currentVideoSessionId) {
                                            if (videoSdkType === 'sanTi') {
                                                $('.video-meeting-btn').css('display', 'none')
                                                $('#selfvideo', container).hide();
                                                $('#othervideo', container).hide();
                                                $('.meeting', container).hide();
                                                currentVideoSession.locked = false
                                                currentVideoSession.videoActive = false
                                                isVideoCallIng = false
                                                if (globalMeeting) {
                                                    globalMeeting.leaveRoom()
                                                }
                                            } else if (videoSdkType === 'juFeng') {
                                                $('.video-meeting-btn').css('display', 'none')
                                                $('#selfvideo', container).hide();
                                                $('#othervideo', container).hide();
                                                $('.jufengMeeting', container).hide();
                                                currentVideoSession.locked = false
                                                currentVideoSession.videoActive = false
                                                isVideoCallIng = false
                                                if (globalMeeting) {
                                                    globalMeeting.leaveRoom()
                                                }
                                            }
                                        }

                                    }
                                    if (sessionId == currentSessionId) {
                                        appendTextToDiv(formatFeedbackMessage(noticeMessage));
                                    }
                                }
                            }
                        }

                        if (sessionId == currentSessionId) {
                            refreshPreMessage(session.tempMessage, sessionId);
                        }

                        thisSession.previewMessage = session.tempMessage;

                        let newMessages = session.newMessages;
                        let preMsgUserId = thisSession.preMsgUserId;
                        if (newMessages.length > 0) {
                            if (sessionId == currentSessionId) {
                                $('.has-read', container).text(i18n.message.hasRead);
                            } else {
                                if (session.allMessage) {
                                    session.allMessages.forEach(function (message) {
                                        if (message.read) {
                                            message.read = i18n.message.hasRead;
                                            message.displayMessage = '';
                                        }
                                    });
                                }
                            }
                        }
                        for (let messageIndex = 0; messageIndex < newMessages.length; messageIndex++) {
                            let message = newMessages[messageIndex];
                            let text = message.text;
                            if (text) {
                                text = tools.parseDisplayContent(text);
                                if (!text) {
                                    continue;
                                }
                                // 新消息收到后发出事件通知框架
                                if (newMessageNotifyEnabled) {
                                    notifyProjectEvent($CONST.ChatEvent.NEW_MESSAGE_RECEIVED || 'NEW_MESSAGE_RECEIVED', {
                                        sessionId: sessionId,
                                        message: message
                                    });
                                }

                                var userId = message.userId;
                                var userName = message.userName;
                                //判断客户是不是有变更，userType必须是1也就是客户的时候才考虑
                                if (userId != thisSession.clientId && message.userType == 1) {
                                    updateClientForSession(thisSession, userId, userName);
                                }


                                if (c.highlightWords) {
                                    c.highlightWords.forEach(function (hw) {
                                        var hwReg = new RegExp(hw, 'g');
                                        text = text.replace(hwReg, '<span class="dw-highlight">' + hw + '</span>');
                                    });
                                }
                                try {
                                    message.reference = JSON.parse(message.reference)
                                } catch (e) { }

                                var newMessage = addMessageToSession(sessionId, {
                                    id: userId,
                                    name: userName,
                                    monitorSender: message.monitorSender
                                }, {
                                    id: message.revokeId || message.id,
                                    text: text,
                                    translate: message.translateMessage,
                                    quoteMessageText: (message.reference && message.reference.content) ? message.reference.content.displayText : undefined,
                                    quoteMessageId: message.reference ? message.reference._id : undefined,
                                    quoteMessageSender: (message.reference && message.reference.user) ? message.reference.user.name : ''
                                }, Tools.getFormattedTime(message.postTime || new Date().getTime(), 1));
                                if (message.extra) newMessage.extra = message.extra;
                                // 每条新消息都发出通知
                                showMessageOnItem(sessionId, text);
                                if (sessionId == currentSessionId) {
                                    refreshPreMessage();
                                    removeTypingNotice();
                                    $F.log("Chat - processJSONPullReturn", "pull message " + userId + " : " + text);
                                    var displayMessage = getDisplayMessage(newMessage, thisSession);
                                    appendTextToDiv(displayMessage);
                                    //setLastMessageReceivedTime ('最后一条消息时间 ' + currentTime3());
                                }
                                newMassageSoundEnabled && soundManager.play('newMessageSound');
                                preMsgUserId = userId;

                                // 如果是客户或者监控的时候去暂停发送AFK
                                if (message.userType == 1) {
                                    if (!thisSession.userInWaiting) {
                                        setUserInWaiting(thisSession, true);
                                        thisSession.lastUserMessageReceivedTime = new Date().getTime();
                                    } else {
                                        thisSession.autoReplyMessageTimes = 0;
                                    }
                                    thisSession.lastAutoReplyMessageTime = new Date().getTime();
                                    thisSession.hasUserNoResponseNotified = false;
                                } else if (message.monitorSender && message.monitorSender !== '') {
                                    if (thisSession.userInWaiting) {
                                        setUserInWaiting(thisSession, false);
                                        thisSession.lastAgentMessageSentTime = new Date().getTime();
                                        // if (!session.firstReply) {
                                        //     session.firstReply = true;
                                        //     // 坐席第一次回复消息时候触发此消息
                                        //     notifyProjectEvent($CONST.ChatEvent.CHAT_SESSION_FIRST_REPLY || 'CHAT_SESSION_FIRST_REPLY', {
                                        //         sessionId: sessionId
                                        //     });
                                        // }
                                    }
                                }
                            }
                        }
                        thisSession.preMsgUserId = preMsgUserId;

                        if (session.ifShouldEnd) {
                            if (thisSession && thisSession.active) {
                                var feedback = i18n.feedback.clientLoggedOut;
                                addMessageToSession(sessionId, {
                                    id: "-1",
                                    name: null
                                }, {
                                    text: formatFeedbackMessage(feedback),
                                    type: MESSAGE_TYPE.SYSTEM
                                });
                                thisSession.active = false;
                                thisSession.endTime = new Date().getTime();
                                thisSession.previewMessage = '';
                                $F.log('Chat - processJSONPullReturn', 'Session shouldEnd, set session ' + sessionId + ' active false');
                                if (sessionId == currentSessionId) {
                                    appendTextToDiv(formatFeedbackMessage(feedback));
                                }
                                disableSession(sessionId);
                                refreshSessionStat();
                                sinkSession(sessionId);
                                checkReservedStatusWhenSessionEnd();
                            }
                        }
                    } //end for session in sessions
                } //end if sessions
                //有视频
                if (receivedInfo.videoMeetings) {
                    let videoMeetings = receivedInfo.videoMeetings;
                    videoActiveIng = false; //为true则表示，坐席正在和其中一个客户视频
                    for (let key in sessionsMap) {
                        if (sessionsMap.hasOwnProperty(key)) {
                            if (sessionsMap[key].videoActive === true) {
                                videoActiveIng = true;
                                break;
                            }
                        }
                    }
                    //pull会话
                    videoMeetings.forEach(function (videoMeet) {
                        var session = sessionsMap["session" + videoMeet.sessionId];
                        if (videoSdkType === 'sanTi') {
                            if (videoMeet.actionType === 'createRequest' && videoMeet.from === 1) { //from是1代表是客户创建的房间
                                // 如果坐席正在和一个客户视频
                                if (chatPanelIsHidden()) {
                                    $('header .c_chat').click()
                                }
                                if (isVideoCallIng) {
                                    const sentInfoJSON = JSON.stringify({
                                        sessionId: videoMeet.sessionId,
                                        roomId: videoMeet.roomId,
                                        actionType: "chatingInRoom",
                                        from: 2
                                    });
                                    callService({
                                        data: {
                                            requestType: "videoMeetingEvent",
                                            data: sentInfoJSON
                                        },
                                        success: data => {
                                            sendMessage(`${i18n.videoMeetingRecord.CallIng} [videoMeet]`, videoMeet.sessionId)
                                        }
                                    }, true).fail(function (e) {
                                        $F.err('chatingInRoomError', e)
                                    });
                                } else {
                                    $('.meeting.videoMeet .waitAccept', container).show();
                                    $('.meeting', container).show()
                                    $('.meeting.videoMeet .video', container).hide()
                                    $('.meeting.videoMeet .audio', container).show()
                                    $('.meeting .caller .audio .setCon .users').html(session.client.name)
                                    $('.meeting .caller .video .setCon .users').html(session.client.name)
                                    session.locked = true
                                    session.videoActive = true
                                    acceptRoomId = videoMeet.roomId
                                    acceptVideoSessionId = videoMeet.sessionId
                                    clearSessionRequest(videoMeet.sessionId, videoMeet.roomId)
                                    videoNoResponseTimer = setTimeout(() => {
                                        $('#selfvideo', container).hide();
                                        $('#othervideo', container).hide();
                                        $('.meeting', container).hide();
                                        if (session.locked) {
                                            session.lastAgentMessageSentTime = new Date().getTime();
                                        }
                                        session.locked = false
                                        session.videoActive = false
                                        unAnsweredVedioRequest(videoMeet.sessionId, videoMeet.roomId)
                                        if (globalMeeting) {
                                            globalMeeting.leaveRoom()
                                        }
                                    }, videoNoResponseTime)
                                }
                            } else if (videoMeet.actionType === 'createRequest' && videoMeet.from === 2) {
                                isVideoCallIng = false
                            } else if (videoMeet.actionType === 'cancelRequest' && videoMeet.from === 2) {
                                if (session.locked) {
                                    session.lastAgentMessageSentTime = new Date().getTime();
                                }
                                session.locked = false
                                session.videoActive = false
                            } else if (videoMeet.actionType === 'leaveRoom' && videoMeet.from === 2) {
                                $('.video-meeting-btn').css("display", "none");
                                if (session.locked) {
                                    session.lastAgentMessageSentTime = new Date().getTime();
                                }
                                session.locked = false
                                session.videoActive = false
                            } else if (videoMeet.actionType === 'cancelRequest' && videoMeet.from === 1) {
                                isVideoCallIng = false
                                if (videoNoResponseTimer) clearTimeout(videoNoResponseTimer)
                                $('#selfvideo', container).hide();
                                $('#othervideo', container).hide();
                                $('.meeting', container).hide();
                                if (session.locked) {
                                    session.lastAgentMessageSentTime = new Date().getTime();
                                }
                                session.locked = false
                                session.videoActive = false
                                clearSessionRequest(videoMeet.sessionId, videoMeet.roomId)
                            } else if (videoMeet.actionType === 'acceptRequest' && videoMeet.from === 1) { //1代表客户端接受的视频
                                meetingStartTime = new Date().getTime()
                                isVideoCallIng = true
                                session.locked = true
                                session.videoActive = true
                                $('.meeting.videoMeet .waitVideo', container).show();
                                $('#selfvideo', container).show();
                                $('#othervideo', container).show();
                                clearSessionRequest(videoMeet.sessionId, videoMeet.roomId)
                            } else if (videoMeet.actionType === 'leaveRoom' && videoMeet.from === 1) {
                                isVideoCallIng = false
                                if (session.locked) {
                                    session.lastAgentMessageSentTime = new Date().getTime();
                                }
                                session.videoActive = false
                                session.locked = false
                                $('.video-meeting-btn').css('display', 'none')
                                $('#selfvideo', container).hide();
                                $('#othervideo', container).hide();
                                $('.meeting', container).hide();
                                if (globalMeeting) {
                                    globalMeeting.leaveRoom()
                                }
                                clearSessionRequest(videoMeet.sessionId, videoMeet.roomId)
                            } else if (videoMeet.actionType === 'refuseRequest' && videoMeet.from === 1) {
                                isVideoCallIng = false
                                $('.video-meeting-btn').css('display', 'none')
                                $('#selfvideo', container).hide();
                                $('#othervideo', container).hide();
                                $('.meeting', container).hide();
                                if (session.locked) {
                                    session.lastAgentMessageSentTime = new Date().getTime();
                                }
                                session.locked = false
                                session.videoActive = false
                                if (globalMeeting) {
                                    globalMeeting.leaveRoom()
                                }
                                clearSessionRequest(videoMeet.sessionId, videoMeet.roomId)
                            } else if (videoMeet.actionType === 'noResponse' && videoMeet.from === 1) {
                                isVideoCallIng = false
                                $('.video-meeting-btn').css('display', 'none')
                                $('#selfvideo', container).hide();
                                $('#othervideo', container).hide();
                                $('.meeting', container).hide();
                                if (session.locked) {
                                    session.lastAgentMessageSentTime = new Date().getTime();
                                }
                                session.locked = false
                                session.videoActive = false
                                if (globalMeeting) {
                                    globalMeeting.leaveRoom()
                                }
                                clearSessionRequest(videoMeet.sessionId, videoMeet.roomId)
                            }
                        } else if (videoSdkType === 'juFeng') {
                            if (videoMeet.actionType === 'createRequest' && videoMeet.from === 1) { //from是1代表是客户创建的房间
                                // 如果坐席正在和一个客户视频
                                if (chatPanelIsHidden()) {
                                    $('header .c_chat').click()
                                }
                                if (isVideoCallIng) {
                                    const sentInfoJSON = JSON.stringify({
                                        sessionId: videoMeet.sessionId,
                                        roomId: videoMeet.roomId,
                                        actionType: "chatingInRoom",
                                        from: 2
                                    });
                                    callService({
                                        data: {
                                            requestType: "videoMeetingEvent",
                                            data: sentInfoJSON
                                        },
                                        success: data => {
                                            sendMessage(`${i18n.videoMeetingRecord.CallIng} [videoMeet]`, videoMeet.sessionId)
                                        }
                                    }, true).fail(function (e) {
                                        $F.err('chatingInRoomError', e)
                                    });
                                } else {
                                    $('.jufengMeeting.videoMeet .waitAccept', container).show();
                                    $('.jufengMeeting', container).show()
                                    $('.jufengMeeting.videoMeet .video', container).hide()
                                    $('.jufengMeeting.videoMeet .audio', container).show()
                                    $('.jufengMeeting .caller .audio .setCon .users').html(session.client.name)
                                    $('.jufengMeeting .caller .video .setCon .users').html(session.client.name)
                                    session.locked = true
                                    session.videoActive = true
                                    acceptRoomId = videoMeet.roomId
                                    acceptVideoSessionId = videoMeet.sessionId
                                    clearSessionRequest(videoMeet.sessionId, videoMeet.roomId)
                                    videoNoResponseTimer = setTimeout(() => {
                                        $('#selfvideo', container).hide();
                                        $('#othervideo', container).hide();
                                        $('.jufengMeeting', container).hide();
                                        if (session.locked) {
                                            session.lastAgentMessageSentTime = new Date().getTime();
                                        }
                                        session.locked = false
                                        session.videoActive = false
                                        unAnsweredVedioRequest(videoMeet.sessionId, videoMeet.roomId)
                                        if (globalMeeting) {
                                            globalMeeting.leaveRoom()
                                        }
                                    }, videoNoResponseTime)
                                }
                            } else if (videoMeet.actionType === 'createRequest' && videoMeet.from === 2) {
                                isVideoCallIng = false
                            } else if (videoMeet.actionType === 'cancelRequest' && videoMeet.from === 2) {
                                if (session.locked) {
                                    session.lastAgentMessageSentTime = new Date().getTime();
                                }
                                session.locked = false
                                session.videoActive = false
                            } else if (videoMeet.actionType === 'leaveRoom' && videoMeet.from === 2) {
                                if (session.locked) {
                                    session.lastAgentMessageSentTime = new Date().getTime();
                                }
                                session.locked = false
                                session.videoActive = false
                            } else if (videoMeet.actionType === 'cancelRequest' && videoMeet.from === 1) {
                                isVideoCallIng = false
                                if (videoNoResponseTimer) clearTimeout(videoNoResponseTimer)
                                $('#selfvideo', container).hide();
                                $('#othervideo', container).hide();
                                $('.jufengMeeting', container).hide();
                                if (session.locked) {
                                    session.lastAgentMessageSentTime = new Date().getTime();
                                }
                                session.locked = false
                                session.videoActive = false
                                clearSessionRequest(videoMeet.sessionId, videoMeet.roomId)
                            } else if (videoMeet.actionType === 'acceptRequest' && videoMeet.from === 1) { //1代表客户端接受的视频
                                isVideoCallIng = true
                                meetingStartTime = new Date().getTime()
                                session.locked = true
                                session.videoActive = true
                                $('.jufengMeeting.videoMeet .waitVideo', container).show();
                                $('#selfvideo', container).show();
                                $('#othervideo', container).show();
                                clearSessionRequest(videoMeet.sessionId, videoMeet.roomId)
                            } else if (videoMeet.actionType === 'leaveRoom' && videoMeet.from === 1) {
                                isVideoCallIng = false
                                if (session.locked) {
                                    session.lastAgentMessageSentTime = new Date().getTime();
                                }
                                session.videoActive = false
                                session.locked = false
                                $('.video-meeting-btn').css('display', 'none')
                                $('#selfvideo', container).hide();
                                $('#othervideo', container).hide();
                                $('.jufengMeeting', container).hide();
                                if (globalMeeting) {
                                    globalMeeting.leaveRoom()
                                }
                                clearSessionRequest(videoMeet.sessionId, videoMeet.roomId)
                            } else if (videoMeet.actionType === 'refuseRequest' && videoMeet.from === 1) {
                                isVideoCallIng = false
                                $('.video-meeting-btn').css('display', 'none')
                                $('#selfvideo', container).hide();
                                $('#othervideo', container).hide();
                                $('.jufengMeeting', container).hide();
                                if (session.locked) {
                                    session.lastAgentMessageSentTime = new Date().getTime();
                                }
                                session.locked = false
                                session.videoActive = false
                                if (globalMeeting) {
                                    globalMeeting.leaveRoom()
                                }
                                clearSessionRequest(videoMeet.sessionId, videoMeet.roomId)
                            } else if (videoMeet.actionType === 'noResponse' && videoMeet.from === 1) {
                                isVideoCallIng = false
                                $('.video-meeting-btn').css('display', 'none')
                                $('#selfvideo', container).hide();
                                $('#othervideo', container).hide();
                                $('.jufengMeeting', container).hide();
                                if (session.locked) {
                                    session.lastAgentMessageSentTime = new Date().getTime();
                                }
                                session.locked = false
                                session.videoActive = false
                                if (globalMeeting) {
                                    globalMeeting.leaveRoom()
                                }
                                clearSessionRequest(videoMeet.sessionId, videoMeet.roomId)
                            }
                        }
                    })
                }

                // 坐席实时查看排队人数
                if (c.configs.agentRealtimeQueueMonitorEnable) {
                    if (receivedInfo.monitorInfo) {
                        $('.stat_info .queue_waiting_requests', container).text(receivedInfo.monitorInfo.waitingRequests || 0);
                    }
                }
            } else if (jsonResponse.status === 'relogined') {
                alert(i18n.error.reloggedInAlert);
                c.closeChat();
            } else if (jsonResponse.code === -38) {
                alert("系统检测到当前浏览器已经打开访客界面，当前网聊首页将关闭！请关闭访客界面后再次打开网聊首页");
                setTimeout(function () {
                    c.logoutAndClose();
                }, 3000);
            }
        }

        function updatePermSessions(callback) {
            callService({
                data: {
                    requestType: "getServedClientSessions"
                }
            }).done(function (data) {
                if (data.status === "success") {
                    //如果存在持久会话，则直接显示相关会话
                    var permSessions = data.sessions,
                        ver = new Date().getTime();
                    if (permSessions && permSessions.length > 0) {
                        $.each(permSessions, function (i, session) {
                            var sessionId = session.id;
                            if (sessionsMap['session' + sessionId] && sessionsMap['session' + sessionId].active) {
                                sessionsMap['session' + sessionId].ver = ver;
                            } else {
                                $F.log("Chat - perm session", "Load permanent session " + sessionId);
                                var client = session.client;
                                var newSession = createSession(sessionId, client, -1, '', {});
                                newSession.permFlag = 1;
                                newSession.ver = ver;
                                sessionsMap["session" + sessionId] = newSession;
                                sessionIdsArr[sessionIdsArr.length] = sessionId;
                                lastCreatedSessionId = "session" + sessionId;
                                setupClientDisplayName(client);
                                setupClientIcon(client);
                                createNewTab('sessionTab_' + sessionId, client);
                            }
                        });
                    }
                    //有的持久会话关系去除了，界面上会话也需要删除
                    $.each(sessionsMap, function (key, session) {
                        if (session.permFlag === 1 && session.ver !== ver) {
                            removeTabAndSession(session.id);
                        }
                    });
                    refreshSessionStat();
                } else {
                    $F.err('Chat - getServedClientSessions', i18n.error.servedClientSessionsListFailed);
                }
            }).fail(function () {
                $F.err('Chat - getServedClientSessions', i18n.error.servedClientSessionsListFailed);
            }).always(function () {
                callback && callback();
            });
        }

        function timedUpdatePermSessions() {
            updatePermSessions(function () {
                updatePermSessionsTimer = setTimeout(function () {
                    if (c.logined) {
                        timedUpdatePermSessions();
                    }
                }, timeoutForUpdatePermSessions);
            })
        }

        function sendRequestResponse(requestId, msg) {
            var sentInfoJSON = JSON.stringify({
                actionType: "respondToRequest",
                senderId: c.configs.agentId,
                senderName: c.staff.displayName,
                chatRequestId: requestId,
                message: msg
            });

            $F.log("Chat - sendRequestResponse", sentInfoJSON);
            callService({
                data: {
                    requestType: "send",
                    data: sentInfoJSON
                },
                success: data => {
                    processRespondToRequestReturn(data);
                }
            }, true).fail(function (e) {
                if (msg === 'yes') {
                    if (e.statusText === 'timeout') {
                        // 如果超时了，那么尝试再去请求服务器看这个坐席现在有哪些会话
                        recoverSession(requestId);
                    }
                }
            });
        }

        function recoverSession(requestId) {
            callService({
                data: {
                    requestType: "recoverSession",
                    requestId: requestId
                }
            }).done(function (data) {
                if (data && data.status === 'success') {
                    if (data.result.code === 1) {
                        var s = data.result.session,
                            sessionId = s.sessionId,
                            client = s.client,
                            queueId = s.queueId,
                            queueName = s.queueName,
                            sessionParams = s.sessionParams,
                            eventGuid = s.eventGuid;

                        if (sessionsMap["session" + sessionId] === undefined) {
                            $F.log("Chat - recoverSession", 'Recover session [' + sessionId + ']');
                            var newSession = createSession(sessionId, client, queueId, queueName, sessionParams, eventGuid);
                            sessionsMap["session" + sessionId] = newSession;
                            sessionIdsArr[sessionIdsArr.length] = sessionId;
                            createNewTab("sessionTab_" + sessionId, client);
                            lastCreatedSessionId = "session" + sessionId;

                            var historySessions = data.result.historySessions;
                            if (historySessions) {
                                $.each(historySessions, function (i, hs) {
                                    $.each(hs.newMessages, function (j, message) {
                                        var text = message.text;
                                        if (!Robot.isRobot(message.userId)) { //非机器人时候才需要parse
                                            text = tools.parseDisplayContent(text);
                                        } else {
                                            text = Robot.parseRobotMessage(text, message.userId);
                                        }
                                        if (text) {
                                            try {
                                                message.reference = JSON.parse(message.reference)
                                            } catch (e) { }
                                            if (message.messageType === NOTICE_TYPE.CUSTOM) {
                                                addMessageToSession(sessionId, {
                                                    id: "-1",
                                                    name: null
                                                }, {
                                                    text: formatFeedbackMessage(text),
                                                    type: MESSAGE_TYPE.SYSTEM
                                                });
                                            } else {
                                                addMessageToSession(sessionId, {
                                                    id: message.userId,
                                                    name: message.userName,
                                                    icon: message.icon
                                                }, {
                                                    id: message.revokeId || message.id,
                                                    text: text,
                                                    revokeFlag: message.revokeFlag,
                                                    history: true,
                                                    translate: message.translateMessage,
                                                    quoteMessageText: (message.reference && message.reference.content) ? message.reference.content.displayText : undefined,
                                                    quoteMessageId: message.reference ? message.reference._id : undefined,
                                                    quoteMessageSender: (message.reference && message.reference.user) ? message.reference.user.name : ''
                                                }, Tools.getFormattedTime(message.postTime));
                                            }
                                        }
                                    });
                                    var sessionEndTime = '';
                                    if (hs.sessionInfo && hs.sessionInfo.endTime) {
                                        sessionEndTime = ' [' + new Date(hs.sessionInfo.endTime).format('yyyy-MM-dd hh:mm:ss') + ']';
                                    }
                                    addMessageToSession(sessionId, {
                                        id: "-1",
                                        name: null
                                    }, {
                                        text: '<div class="notice"><span class="line">————————</span>会话' + hs.id + sessionEndTime + '<span class="line">————————</span></div>',
                                        history: true,
                                        type: MESSAGE_TYPE.SYSTEM
                                    });
                                });
                            }

                            sendWelcomeMessage(newSession, queueId);
                        }
                    }
                }
            }).fail(function (e) {
                if (e.statusText === 'timeout') {
                    recoverSession(requestId);
                }
            });
        }

        function processRespondToRequestReturn(jsonResponse) {
            // fixme
            console.log('Chat - processRespondToRequestReturn', JSON.stringify(jsonResponse));
            if (jsonResponse.status === 'success') {
                var sendResponse = jsonResponse.sendResponse;
                if (sendResponse) {
                    var result = sendResponse.result;
                    var feedbackMessage = sendResponse.feedbackMessage;
                    if (result === 1 || sendResponse.ifSucceed) { //ifSucceed已经不使用，这里为了兼容老的chat服务
                        //接起会话成功如果是当前的聊天界面是隐藏的，那么需要自动展示出来
                        var isCurrentSession = false,
                            sessionId = sendResponse.sessionId,
                            clientId = sendResponse.clientId,
                            queueId = sendResponse.queueId,
                            queueName = sendResponse.queueName,
                            sessionParams = sendResponse.sessionParams || {},
                            usersInSession = sendResponse.usersInSession,
                            eventGuid = sendResponse.eventGuid,
                            client = sendResponse.client,
                            from = sendResponse.from;
                        setupClientIcon(client);
                        setupClientDisplayName(client);
                        if (c.configs.autoCloseTabInMultiRequest) {
                            $.each(sessionsMap, function (i, session) {
                                if (session.clientId === clientId && sessionsMap["session" + sessionId] === undefined) {
                                    $F.log("Chat - processRespondToRequestReturn", "removeTabAndSession: " + session.id + " cause a new request is coming from the same client.");
                                    removeTabAndSession(session.id);
                                }
                            });
                        }

                        if (sessionsMap["session" + sessionId] === undefined) {
                            var newSession = createSession(sessionId, client, queueId, queueName, sessionParams, eventGuid);
                            newSession.sessionType = sessionParams.sessionType;
                            sessionsMap["session" + sessionId] = newSession;
                            sessionIdsArr[sessionIdsArr.length] = sessionId;
                            createNewTab("sessionTab_" + sessionId, client);
                        } else { //session already exists: this happens when the session was transferred to other agent before
                            var tab = $('#sessionTab_' + sessionId, container),
                                curSession = sessionsMap["session" + sessionId];
                            if (tab.length === 0) {
                                sessionIdsArr[sessionIdsArr.length] = sessionId; //if tab was destroyed, then the sessionId was removed too
                                createNewTab("sessionTab_" + sessionId, client);
                            } else {
                                enableSession(sessionId);
                                isCurrentSession = true;
                            }
                            curSession.active = true;
                            curSession.userCount = 0;
                        }
                        var sessionCur = sessionsMap["session" + sessionId];
                        lastCreatedSessionId = "session" + sessionId;

                        var historySessions = sendResponse.historySessions;
                        if (historySessions) {
                            $.each(historySessions, function (i, hs) {
                                $.each(hs.newMessages, function (j, message) {
                                    var text = message.text;
                                    if (!Robot.isRobot(message.userId)) { //非机器人时候才需要parse
                                        text = tools.parseDisplayContent(text);
                                    } else {
                                        text = Robot.parseRobotMessage(text, message.userId);
                                    }
                                    if (text) {
                                        try {
                                            message.reference = JSON.parse(message.reference)
                                        } catch (e) { }
                                        if (message.messageType === NOTICE_TYPE.CUSTOM) {
                                            addMessageToSession(sessionId, {
                                                id: "-1",
                                                name: null
                                            }, {
                                                text: formatFeedbackMessage(text),
                                                type: MESSAGE_TYPE.SYSTEM
                                            });
                                        } else {
                                            addMessageToSession(sessionId, {
                                                id: message.userId,
                                                name: message.userName,
                                                icon: message.icon
                                            }, {
                                                id: message.revokeId || message.id,
                                                text: text,
                                                translate: message.translateMessage,
                                                revokeFlag: message.revokeFlag,
                                                history: true,
                                                quoteMessageText: (message.reference && message.reference.content) ? message.reference.content.displayText : undefined,
                                                quoteMessageId: message.reference ? message.reference._id : undefined,
                                                quoteMessageSender: (message.reference && message.reference.user) ? message.reference.user.name : ''
                                            }, Tools.getFormattedTime(message.postTime));
                                        }
                                    }
                                });
                                var sessionEndTime = '';
                                if (hs.sessionInfo && hs.sessionInfo.endTime) {
                                    sessionEndTime = ' [' + new Date(hs.sessionInfo.endTime).format('yyyy-MM-dd hh:mm:ss') + ']';
                                }
                                addMessageToSession(sessionId, {
                                    id: "-1",
                                    name: null
                                }, {
                                    text: '<div class="notice"><span class="line">————————</span>' + i18n.session + hs.id + sessionEndTime + '<span class="line">————————</span></div>',
                                    history: true,
                                    type: MESSAGE_TYPE.SYSTEM
                                });
                            });
                        }

                        addMessageToSession(sessionId, {
                            id: "-1",
                            name: null
                        }, {
                            text: formatFeedbackMessage(feedbackMessage),
                            type: MESSAGE_TYPE.SYSTEM
                        });
                        $.each(usersInSession, function (i, user) {
                            sessionCur.users[user.id] = new User(user.id, user.firstName, user.lastName, user.alias, user.headImg);
                            sessionCur.userCount++;
                        });

                        //do not auto switch new session tab
                        //switchTabAndSession(tab);
                        if (!currentSessionId) {
                            switchTabAndSession('sessionTab_' + sessionId);
                        }

                        if (isCurrentSession) { // 转接过来的会话，同时当前tab就在这个会话上
                            switchTabAndSession('sessionTab_' + sessionId, true); // 需要调用一把switchTabAndSession，把转接之前历史消息展示出来
                        }


                        if (sessionIdNew) { // 新进来的会话都加载历史
                            switchTabAndSession('sessionTab_' + sessionId, true); // 新进来的会话都加载历史
                        }

                        //auto send welcome messages
                        sendWelcomeMessage(sessionCur, queueId);

                        // 允许音视频
                        getPropertyByQueue('allowAudioVisual', queueId).then(msg => {
                            if (msg) {
                                $('.video-meeting-btn', container).click(function (e) {
                                    e.stopPropagation();
                                    if (videoSdkType === 'sanTi') {
                                        $('.meeting', container).css("display", "inline-block");
                                        $('.video-meeting-btn').css('display', 'none')
                                    } else if (videoSdkType === 'juFeng') {
                                        $('.jufengMeeting', container).css("display", "inline-block");
                                        $('.video-meeting-btn').css('display', 'none')
                                    }

                                })
                                videoNoResponseTime = msg.videoNoResponseTime
                                if (msg.type && msg.type === 'tencent') {
                                    require(['js/plugins/chat/chat.tencent.meeting'], function (Meeting) {
                                        Meeting.loadTencentMeet(msg, sessionId, sessionCur.client.name, container);
                                    });
                                } else if (msg.type && msg.type === 'sanTi') {
                                    $('.meeting .caller .video .forwardBox').click(function (event) {
                                        $('.meeting', container).hide();
                                        $('.video-meeting-btn').css("display", "inline-block");
                                    });
                                    videoSdkType = 'sanTi'
                                    require(['js/plugins/chat/chat.sanTi.meeting'], function (Meeting) {
                                        globalMeeting = Meeting;
                                        $('.chat .chat_video', container).show();
                                        $('.chat .chat_video', container).off('click')
                                        $('.chat .chat_video').click(function (event) {
                                            $('.meeting.videoMeet .waitAccept', container).show();
                                            if (!isVideoCallIng) {
                                                currentVideoSessionId = currentSessionId
                                                sessionsMap["session" + currentSessionId].locked = true
                                                sessionsMap["session" + currentSessionId].videoActive = true
                                                Meeting.loadSanTiMeet(sendMessage, chatServerAddr, msg, currentSessionId, sessionsMap["session" + currentSessionId].client.name, container);
                                            } else {
                                                $F.notify('正在通话中不允许点击！');
                                            }
                                        });
                                        $('.meeting .acceptVCall', container).off('click')
                                        //接受视频
                                        $('.meeting .acceptVCall', container).click(function (e) {
                                            e.stopPropagation();
                                            meetingStartTime = new Date().getTime()
                                            if (videoNoResponseTimer) clearTimeout(videoNoResponseTimer)
                                            var acceptSession = sessionsMap["session" + acceptVideoSessionId];
                                            $('.meeting.videoMeet .audio', container).hide();
                                            $('.meeting.videoMeet .video', container).show();
                                            $('.meeting .video .titleBox .title').html('视频通话 ' + ` (${acceptSession.client.name})`)
                                            currentVideoSessionId = acceptVideoSessionId
                                            const sentInfoJSON = JSON.stringify({
                                                sessionId: acceptVideoSessionId,
                                                roomId: acceptRoomId,
                                                actionType: "acceptRequest",
                                                from: 2
                                            });
                                            callService({
                                                data: {
                                                    requestType: "videoMeetingEvent",
                                                    data: sentInfoJSON
                                                },
                                                success: data => {
                                                    acceptSession.locked = true
                                                    acceptSession.videoActive = true
                                                    Meeting.loadSanTiMeet(sendMessage, chatServerAddr, msg, acceptVideoSessionId, acceptSession.client.name, container, acceptRoomId);
                                                    isVideoCallIng = true
                                                }
                                            }, true).fail(function (e) {
                                                $F.err('acceptRequestError', e)
                                            });
                                        });
                                        $('.meeting .rejectVCall', container).off('click')
                                        //视频拒接c
                                        $('.meeting .rejectVCall', container).click(function (e) {
                                            e.stopPropagation();
                                            if (videoNoResponseTimer) clearTimeout(videoNoResponseTimer)
                                            let acceptSession = sessionsMap["session" + acceptVideoSessionId];
                                            $('.meeting', container).hide()
                                            const sentInfoJSON = JSON.stringify({
                                                sessionId: acceptVideoSessionId,
                                                roomId: acceptRoomId,
                                                actionType: "refuseRequest",
                                                from: 2,
                                            });
                                            callService({
                                                data: {
                                                    requestType: "videoMeetingEvent",
                                                    data: sentInfoJSON
                                                },
                                                success: data => {
                                                    acceptSession.locked = false
                                                    acceptSession.videoActive = false
                                                    sendMessage(`${i18n.videoMeetingRecord.Refuse} [videoMeet]`, acceptVideoSessionId)
                                                }
                                            }, true).fail(function (e) {
                                                $F.err('refuseRequestError', e)
                                            });

                                        });
                                        $('.meeting .releaseVCall', container).off('click')
                                        //视频挂断
                                        $('.meeting .releaseVCall', container).click(function (e) {
                                            e.stopPropagation();
                                            $('.video-meeting-btn').css('display', 'none')
                                            if (isVideoCallIng) { //挂断电话
                                                $('#selfvideo', container).hide();
                                                $('#othervideo', container).hide();
                                                $('.meeting', container).hide();
                                                Meeting.levelRoomRequest()
                                                Meeting.leaveRoom()
                                                if (sessionsMap["session" + currentVideoSessionId].locked) {
                                                    sessionsMap["session" + currentVideoSessionId].lastAgentMessageSentTime = new Date().getTime();
                                                }
                                                sessionsMap["session" + currentVideoSessionId].locked = false
                                                sessionsMap["session" + currentVideoSessionId].videoActive = false
                                                sendMessage(`${i18n.videoMeetingRecord.Connect} ${formatTimestampDiff(new Date().getTime() - meetingStartTime)} [videoMeet]`, acceptVideoSessionId)
                                            } else { //取消电话
                                                $('#selfvideo', container).hide();
                                                $('#othervideo', container).hide();
                                                $('.meeting', container).hide();
                                                Meeting.cancelRequest()
                                                Meeting.leaveRoom()
                                            }
                                            isVideoCallIng = false
                                            sessionsMap["session" + currentVideoSessionId].locked = false
                                        });
                                    });
                                } else if (msg.type && msg.type === 'juFeng') {
                                    $('.jufengMeeting .caller .video .forwardBox').click(function (event) {
                                        $('.jufengMeeting', container).hide();
                                        $('.video-meeting-btn').css("display", "inline-block");
                                    });
                                    videoSdkType = 'juFeng'
                                    require(['js/plugins/chat/chat.jufeng.meeting'], function (Meeting) {
                                        globalMeeting = Meeting;

                                        $('.chat .chat_video', container).show();
                                        $('.chat .chat_video').click(function (event) {
                                            $('.jufengMeeting.videoMeet .waitAccept', container).show();
                                            if (!isVideoCallIng) {
                                                currentVideoSessionId = currentSessionId
                                                sessionsMap["session" + currentSessionId].locked = true
                                                sessionsMap["session" + currentSessionId].videoActive = true
                                                Meeting.loadSanTiMeet(sendMessage, chatServerAddr, msg, currentSessionId, sessionsMap["session" + currentSessionId].client.name, container);
                                            } else {
                                                $F.notify('正在通话中不允许点击！');
                                            }
                                        });
                                        $('.jufengMeeting .acceptVCall', container).off('click')
                                        //接受视频
                                        $('.jufengMeeting .acceptVCall', container).click(function (e) {
                                            e.stopPropagation();
                                            meetingStartTime = new Date().getTime()
                                            if (videoNoResponseTimer) clearTimeout(videoNoResponseTimer)
                                            let acceptSession = sessionsMap["session" + acceptVideoSessionId];
                                            $('.jufengMeeting.videoMeet .audio', container).hide();
                                            $('.jufengMeeting.videoMeet .video', container).show();
                                            $('.jufengMeeting .video .titleBox .title').html('视频通话 ' + ` (${acceptSession.client.name})`)
                                            console.log('acceptRoomId', acceptRoomId)

                                            currentVideoSessionId = acceptVideoSessionId
                                            const sentInfoJSON = JSON.stringify({
                                                sessionId: acceptVideoSessionId,
                                                roomId: acceptRoomId,
                                                actionType: "acceptRequest",
                                                from: 2
                                            });
                                            callService({
                                                data: {
                                                    requestType: "videoMeetingEvent",
                                                    data: sentInfoJSON
                                                },
                                                success: data => {
                                                    acceptSession.locked = true
                                                    acceptSession.videoActive = true
                                                    Meeting.loadSanTiMeet(sendMessage, chatServerAddr, msg, acceptVideoSessionId, acceptSession.client.name, container, acceptRoomId);
                                                    isVideoCallIng = true
                                                }
                                            }, true).fail(function (e) {
                                                $F.err('acceptRequestError', e)
                                            });
                                        });
                                        $('.jufengMeeting .rejectVCall', container).off('click')
                                        //视频拒接c
                                        $('.jufengMeeting .rejectVCall', container).click(function (e) {
                                            e.stopPropagation();
                                            if (videoNoResponseTimer) clearTimeout(videoNoResponseTimer)
                                            let acceptSession = sessionsMap["session" + acceptVideoSessionId];
                                            $('.jufengMeeting', container).hide()
                                            const sentInfoJSON = JSON.stringify({
                                                sessionId: acceptVideoSessionId,
                                                roomId: acceptRoomId,
                                                actionType: "refuseRequest",
                                                from: 2,
                                            });
                                            callService({
                                                data: {
                                                    requestType: "videoMeetingEvent",
                                                    data: sentInfoJSON
                                                },
                                                success: data => {
                                                    acceptSession.locked = false
                                                    acceptSession.videoActive = false
                                                    sendMessage(`${i18n.videoMeetingRecord.Refuse} [videoMeet]`, acceptVideoSessionId)
                                                }
                                            }, true).fail(function (e) {
                                                $F.err('refuseRequestError', e)
                                            });

                                        });
                                        $('.jufengMeeting .releaseVCall', container).off('click')
                                        //视频挂断
                                        $('.jufengMeeting .releaseVCall', container).click(function (e) {
                                            e.stopPropagation();
                                            $('.video-meeting-btn').css("display", "none");
                                            if (isVideoCallIng) { //挂断电话
                                                $('#selfvideo', container).hide();
                                                $('#othervideo', container).hide();
                                                $('.jufengMeeting', container).hide();
                                                Meeting.levelRoomRequest()
                                                // Meeting.leaveRoom()
                                                if (sessionsMap["session" + currentVideoSessionId].locked) {
                                                    sessionsMap["session" + currentVideoSessionId].lastAgentMessageSentTime = new Date().getTime();
                                                }
                                                sessionsMap["session" + currentVideoSessionId].locked = false
                                                sessionsMap["session" + currentVideoSessionId].videoActive = false
                                                sendMessage(`${i18n.videoMeetingRecord.Connect} ${formatTimestampDiff(new Date().getTime() - meetingStartTime)} [videoMeet]`, acceptVideoSessionId)
                                            } else { //取消电话
                                                $('#selfvideo', container).hide();
                                                $('#othervideo', container).hide();
                                                $('.jufengMeeting', container).hide();
                                                Meeting.cancelRequest()
                                                // Meeting.leaveRoom()
                                            }
                                            isVideoCallIng = false
                                            sessionsMap["session" + currentVideoSessionId].locked = false
                                        });

                                    })
                                }
                            } else {
                                $('.chat .chat_audio', container).hide();
                                $('.chat .chat_video', container).hide();
                            }
                        });

                        refreshSessionStat();
                    } else { //failed
                        $F.log("Chat - processRespondToRequestReturn", "request " + sendResponse.chatRequestId + " failed: " + feedbackMessage);
                        $F.notify(i18n.error.requestAlreadyClosed, 0);
                    }
                }
            }
        }

        // let floatingButton =  $('.video-meeting-btn')
        //     let floatingButton =  $('<div class="video-meeting-btn"><a class="fonticon" data-ref="[s]phone"><i class="fas fa-phone" style="font-size: 20px;"></i></a></div>')
        //     $('body').append(floatingButton);
        //     let buttonOffset = {top: 100, left: 1000};
        //     floatingButton.offset(buttonOffset);
        //
        //     // 定义变量用于存储拖动状态
        //     let isDragging = false;
        //     let isDraged = false
        //     let mouseOffset = {top: 0, left: 0};
        //     let originOffset = {top: 0, left: 0};
        //     // 鼠标按下时开始拖动
        //     floatingButton.mousedown(function(e) {
        //         isDragging = true;
        //         isDraged = false
        //         originOffset ={top:e.clientY,left: e.clientX}
        //         mouseOffset = {top: e.clientY - buttonOffset.top, left: e.clientX - buttonOffset.left};
        //     });
        //     // 鼠标移动时更新按钮位置
        //     $(document).mousemove(function(e) {
        //         if (isDragging) {
        //             buttonOffset = {top: e.clientY - mouseOffset.top, left: e.clientX - mouseOffset.left};
        //             floatingButton.offset(buttonOffset);
        //         }
        //     });
        //     // 鼠标松开时停止拖动
        //     $(document).mouseup(function(e) {
        //         if(originOffset.top-e.clientY >5 || originOffset.left-e.clientX >5) {isDraged =true}
        //         isDragging = false;
        //     });


        //发送欢迎语消息
        function sendWelcomeMessage(session, queueId) {
            let sessionId = session.id;
            getPropertyByQueue('autoWelcomeMessage', queueId, session.lang).then(encodedWelcomeMessage => {
                $F.log('Chat - sendWelcomeMessage', 'encodedWelcomeMessage: ' + encodedWelcomeMessage);
                if (encodedWelcomeMessage) {
                    encodedWelcomeMessage = encodedWelcomeMessage.format(session.client.name);
                    var sendData = getPostMessageSentInfoJSONWithSessionId(encodedWelcomeMessage, sessionId, MESSAGE_TYPE.WELCOME_MESSAGE);
                    callService({
                        data: {
                            requestType: "send",
                            data: sendData
                        },
                        success: data => {
                            if (data.status === 'success') {
                                const sendResponse = data.sendResponse;
                                const traslateMessage = sendResponse ? sendResponse.translateMessage : '';
                                var welcomeMessage = addMessageToSession(sessionId, {
                                    id: c.configs.agentId,
                                    name: c.staff.displayName
                                }, {
                                    text: encodedWelcomeMessage,
                                    translate: traslateMessage,
                                    read: i18n.message.unRead,
                                    newMessageId: sendResponse.newMessageId,
                                    type: MESSAGE_TYPE.WELCOME_MESSAGE
                                }, Tools.getFormattedNowTime(true));
                                showMessageOnItem(sessionId, encodedWelcomeMessage);
                                if (sessionId == currentSessionId) {
                                    var displayMsg = getDisplayMessage(welcomeMessage, session);
                                    appendTextToDiv(displayMsg);
                                }
                            }
                        }
                    }, true);
                }
            });
        }

        // 随时间改变列表颜色(time: 秒)
        function changeItemColorByTime(time, sessionId, clear = false) {
            const tab = $('#sessionTab_' + sessionId + '.chat_item')
            if (clear) {
                if (tab && tab[0]) {
                    tab[0].style.backgroundColor = ''
                }
            } else {
                if (chatListTimeSytles && chatListTimeSytles.length && tab && tab[0]) {
                    if (tab[0].className.indexOf('closed') === -1 && tab[0].className.indexOf('active') === -1 && tab[0].className.indexOf('top') === -1) {
                        chatListTimeSytles.forEach((itemStyle, index) => {
                            if (index < chatListTimeSytles.length - 1) {
                                if (time >= itemStyle.time && time < chatListTimeSytles[index + 1].time) {
                                    tab[0].style.backgroundColor = itemStyle.color
                                }
                            } else {
                                if (time >= itemStyle.time) {
                                    tab[0].style.backgroundColor = itemStyle.color
                                }
                            }
                        })
                    }
                }
            }
        }

        function addMessageToSession(sessionId, user, msg, time) {
            var userId = user.id,
                userName = user.name,
                icon = user.icon,
                formattedTime = time || Tools.getFormattedNowTime(true),
                sessionCur = sessionsMap["session" + sessionId],
                allMessages = sessionCur.allMessages,
                msgText = msg,
                msgId = msg.id || 'i' + _messageId++,
                msgRevokeFlag = 0,
                translate = '',
                stateId = '',
                msgType = 0,
                hisMsg = false,
                readMessageId = '',
                monitorSender = '',
                quoteMessage = msg.quoteMessageId ? {
                    text: msg.quoteMessageText,
                    id: msg.quoteMessageId,
                    user: msg.quoteMessageSender
                } : undefined,
                read = '';

            // 防止消息重复(如果当前消息已经存在同一个时间和相同内容,之前的消息移除)
            try {

                for (var i = 0; i < allMessages.length; i++) {
                    var old_userId = allMessages[i].userId;
                    var old_formattedTime = allMessages[i].formattedTime;
                    var old_text = allMessages[i].text;

                    var new_userId = user.id;
                    var new_formattedTime = time;
                    var new_text = msg.text;

                    if (old_userId == new_userId && old_formattedTime == new_formattedTime && old_text == new_text) {
                        console.log('重复消息(' + time + '):', msg.text);
                        allMessages.splice(i, 1); // 将使后面的元素依次前移，数组长度减1
                        i--; // 如果不减，将漏掉一个元素
                    }
                }
            } catch (error) {
                console.error("addMessageToSession 防止消息重复功能异常", error)
            }

            if (typeof msg === 'object') {
                msgText = msg.text;
                translate = msg.translate || '';
                stateId = msg.stateId || '';
                msgId = msg.id || msgId;
                msgRevokeFlag = msg.revokeFlag || msgRevokeFlag;
                monitorSender = user.monitorSender || '';
                hisMsg = msg.history || hisMsg;
                msgType = msg.type || msgType;
                if (sessionCur.sessionType && (sessionCur.sessionType == 9 || sessionCur.sessionType == 3)) {
                    read = msg.read;
                }
                readMessageId = msg.newMessageId || '';
            }
            var message = new Message(msgId, userId, userName, msgText, translate, stateId, formattedTime, icon, msgRevokeFlag, msgType, read, readMessageId, monitorSender, quoteMessage);
            allMessages.push(message);
            if (!hisMsg && (systemNoticeNotify && msg.systemNotice || !msg.systemNotice)) {
                if ("session" + sessionId !== "session" + currentSessionId &&
                    (currentSessionId != null) &&
                    lastCreatedSessionId !== "session" + sessionId) {
                    //更新消息提醒数
                    var reddot = $('#sessionTab_' + sessionId + ' i.web_wechat_reddot_middle', container);
                    if (reddot.length > 0) {
                        var count = parseInt(reddot.html());
                        reddot.html(count + 1);
                    } else {
                        $('<i class="icon web_wechat_reddot_middle">1</i>').appendTo($('#sessionTab_' + sessionId, container));
                    }
                } else {
                    lastCreatedSessionId = null;
                }

                if (!topSessionOnNewMessage || topSessionOnNewMessage == 1) { //移动session到顶部
                    topSession(sessionId);
                } else if (topSessionOnNewMessage == 2) { // 除了自己说的，其他消息来了置顶session
                    if (userId !== c.configs.agentId) {
                        topSession(sessionId);
                    }
                }

                //显示最新消息时间
                // FIXME
                const formatTime = Tools.getFormattedNowTime()
                $('#sessionTab_' + sessionId + ' .ext .attr', container).html(formatTime);

                if (userId === sessionCur.clientId) {
                    if (sessionCur.sessionParams && sessionCur.sessionParams.robotEngine) {
                        var robotEngine = sessionCur.sessionParams.robotEngine;
                        var allowFrom = true;
                        var fromCur = sessionCur.client.from;
                        if (fromCur && robotEngine.extra && robotEngine.extra.from) {
                            var fromAllowsArr = robotEngine.extra.from;
                            if ($.isArray(fromAllowsArr) && fromAllowsArr.length > 0 && $.inArray(fromCur, fromAllowsArr) === -1) {
                                allowFrom = false;
                            }
                        }
                        $F.log('Chat - searchRobot', robotEngine);
                        if (message.robotResponse === undefined) {
                            if (robotEngine.agentSearchFlag === 1 && allowFrom) {
                                //判断当前类型是使用什么形式进行辅助回答的连接
                                var useWebSocket = Robot.userWebSocket(robotEngine.type);
                                $F.log('Chat - searchRobot use webScoket ', useWebSocket);
                                if (useWebSocket) {
                                    sessionCur.lastClientMessage = message;
                                    Robot.doSearch(sessionId, msgText, robotEngine);
                                } else {
                                    //查询机器人
                                    callService({
                                        data: {
                                            requestType: "robotSearch",
                                            sessionId: sessionId,
                                            message: msgText
                                        }
                                    }).done(function (data) {
                                        if (data.status === 'success') {
                                            if (robotAutoAnswerEnabled) {
                                                var richText = false;
                                                if (robotEngine.richText) richText = robotEngine.richText;
                                                var robotAnswer = Robot.parseRobotMessageForAgentAssist(msgText, data.result, robotEngine, sessionsMap["session" + sessionId], c.configs, true, richText);
                                                if (robotAnswer) {
                                                    sendMessage(robotAnswer, sessionId);
                                                }
                                            } else {
                                                message.robotResponse = Robot.parseRobotMessageForAgentAssist(msgText, data.result, robotEngine, sessionsMap["session" + sessionId], c.configs);
                                                if (message.robotResponse) {
                                                    var curSession = sessionsMap["session" + sessionId];
                                                    appendRobotResponse(message, curSession);
                                                }
                                            }
                                        } else {
                                            message.robotResponse = null;
                                        }
                                    }).fail(function () {
                                        $F.err('Chat - robotSearch', i18n.error.getRobotResponseFailed);
                                    });
                                }
                            }
                        }
                    }

                    // 如果是客户发的消息，肯定需要提示
                    notifyMe();
                } else {
                    // 如果是坐席发的消息（这种坐席消息只可能是欢迎语之类的自动提示消息），根据配置来决定要不要提示
                    if ($F.parseInt(project.getParam('CHANMO')) !== 1) {
                        notifyMe();
                    }
                }

                if (chatPanelIsHidden() && userId !== c.configs.agentId) {
                    var reddot = $('i.web_wechat_reddot_middle', project.chatSwitch);
                    if (reddot.length > 0) {
                        var count = parseInt(reddot.html());
                        reddot.html(count + 1);
                    } else {
                        $('<i class="web_wechat_reddot_middle">1</i>').appendTo(project.chatSwitch);
                    }
                }
            }
            return message;
        }

        function appendRobotResponse(message, session) {
            let wrappedResponse = Robot.wrapResponse(message.robotResponse, function (answer) {
                if (session && session.active) {
                    $('.control_panel .content #edit-area', container).html(answer);
                }
            });

            if (session.id == currentSessionId) { // 仅当在当前会话页面上时候才直接添加信息上去
                wrapMessage(wrappedResponse);
                $('.js_message_plain[data-id=' + (message.revokeId || message.id) + ']', container).html(wrappedResponse);
                scrollToBottom('.message_panel');
                //更新消息的displayMessage缓存
                var displayMessageContainer = $('<div>').append(message.displayMessage);
                $('.js_message_plain', displayMessageContainer).html(message.robotResponse);
                message.displayMessage = displayMessageContainer.html();
            }
        }

        function notifyMe() {
            if (!$E.windowOnFocus || chatPanelIsHidden()) {
                H5Utils.notifyMe(i18n.newMessageNotifyBody, i18n.newMessageNotifyTitle, 'js/plugins/chat/images/elite.gif', 'chat', function () {
                    if (!$E.windowOnFocus) {
                        window.open().close();
                        window.focus();
                    }
                    if (chatPanelIsHidden()) {
                        $E.openChatPanel();
                        $('.chat', container).show();
                    }
                });
            }
            if (!$E.windowOnFocus) {
                if (!c.blinkTitleTimer) {
                    c.blinkTitleTimer = setInterval(() => {
                        if (document.title === c.originalTitle) {
                            document.title = i18n.newMessageTitle;
                        } else {
                            document.title = c.originalTitle;
                        }
                    }, 1000)
                }
            }
        }

        /**
         * 在会话列表中 显示最新消息内容
         */
        function showMessageOnItem(sessionId, message) {
            let $htmlDom = $("<div>").append(message);
            if ($htmlDom.find("img:not(.avatar,.qqemoji,.standard-emoji,.location,.custom-emoji,div.message-imgtext .thumb,div.message-card .card-img)").length > 0) { //image
                message = i18n.itemMessage.image;
            }
            message = $('<div>').append(message).text();
            if (message) {
                message = message.replace(/</g, '&lt;').replace(/>/g, '&gt;');
                message = EMOTION.filterEmojis(message);
                if (message.indexOf('[videoMeet]') !== -1) {
                    message = message.replace('[videoMeet]', '<a class="fonticon" data-ref="[l]video"><i class="fal fa-video" style="font-size: 18px;"></i></a>')
                }
                var msg = $('#sessionTab_' + sessionId + ' .info .msg', container);
                if (msg.length > 0) {
                    msg.html(message);
                } else {
                    $('<p class="msg">').html(message).appendTo($('#sessionTab_' + sessionId + ' .info', container));
                }
            }
        }

        /**
         * 根据客户对象获取客户显示的名字
         */
        function setupClientDisplayName(client, newName) {
            if (newName) {
                client.name = newName;
            }
            var clientDisplayName = i18n.unknown;
            if (client.name) {
                clientDisplayName = client.name;
                if (client.nickname) {
                    clientDisplayName = clientDisplayName + '(' + client.nickname + ')';
                }
            } else if (client.nickname) {
                clientDisplayName = client.nickname;
            }

            if (showTypeOnName) {
                if (client.from) {
                    clientDisplayName = '[' + client.from + ']' + clientDisplayName;
                }
                //				if (client.clientType == CLIENT_TYPE.WECHAT) {
                //					clientDisplayName = clientDisplayName + '[微信]';
                //				} else {
                //					if ($F.equalsIgnoreCase(client.from, 'APP')) {
                //						clientDisplayName = clientDisplayName + '[APP]';
                //					} else if ($F.equalsIgnoreCase(client.from, 'MOBILE')) {
                //						clientDisplayName = clientDisplayName + '[手机]';
                //					} else if ($F.equalsIgnoreCase(client.from, 'WECHAT')) {
                //						clientDisplayName = clientDisplayName + '[微信]';
                //					} else {
                //						clientDisplayName = clientDisplayName + '[' + client.from + ']';
                //					}
                //				}
            }
            client.name = clientDisplayName;
            return clientDisplayName;
        }

        /**
         * 根据客户对象获取客户显示头像
         */
        function setupClientIcon(client) {
            if (!client) {
                return ICONS.CLIENT.PC;
            }
            var icon = client.icon;
            if (!icon) {
                // 如果有自定义配置
                if (c.configs.chatUrlFrom && c.configs.chatUrlFrom.length) {
                    const findItem = c.configs.chatUrlFrom.find((item) => {
                        return item.url_from === client.from
                    })
                    if (findItem) {
                        icon = findItem.logo;
                    }
                }
                if (!icon) {
                    if (client.clientType == CLIENT_TYPE.WECHAT) {
                        icon = ICONS.CLIENT.WECHAT;
                    } else {
                        if ($F.equalsIgnoreCase(client.from, 'APP')) {
                            icon = ICONS.CLIENT.PHONE;
                        } else if ($F.equalsIgnoreCase(client.from, 'MOBILE')) {
                            icon = ICONS.CLIENT.PHONE;
                        } else if ($F.equalsIgnoreCase(client.from, 'WECHAT')) {
                            icon = ICONS.CLIENT.WECHAT;
                        } else {
                            icon = c.customIcons[client.from] || ICONS.CLIENT.PC;
                        }
                    }
                }
            } else {
                if (icon.startsWith('livebot/getIcon.do')) {
                    icon = chatServerAddr + "/" + icon;
                }
            }
            client.icon = icon;
            return icon;
        }

        function transLang(str) {
            try {
                switch (str) {
                    case 'zh_TW':
                        return 'cht'
                    default:
                        return str.slice(0, 2)
                }
            } catch (e) {
                return str
            }
        }

        function createNewTab(sessionTabId, client) {
            console.log("chat - createNewTab", "createNewTab with sessionTabId " + sessionTabId + ", client " + JSON.stringify(client));
            var sessionId = sessionTabId.substring(sessionTabId.indexOf('_') + 1),
                session = sessionsMap["session" + sessionId];
            // 如果是允许自动发送翻译内容则需要初始化语言下拉列表
            // setLanguageSelect(transLang(client.lang) || '');
            var chatItem = $('<div id="' + sessionTabId + '" data="' + sessionId + '" class="chat_item">' +
                '<div class="ext">' +
                '<p class="attr"></p>' +
                '</div>' +
                '<div class="avatar">' +
                '<img class="img" src="' + client.icon + '">' +
                '</div>' +
                '<div class="info">' +
                '<h3 class="nickname">' +
                '<span class="nickname_text" title="' + client.name + '">' + client.name + '</span>' +
                '</h3>' +
                '</div>' +
                '<div class="close">' +
                '<i class="fa fa-times-circle"></i>' +
                '</div>' +
                '</div>');
            chatItem.click(function () {
                switchTabAndSession(sessionTabId);
            });
            chatItem.dblclick(function () {
                //发出聊天客户双击事件
                notifyProjectEvent($CONST.ChatEvent.CHAT_SESSION_DOUBLE_CLICKED, {
                    sessionId: sessionId,
                    session: session
                });
            });
            if (session.permFlag == 1) {
                chatItem.find('.close').hide();
            } else {
                chatItem.find('.close').click(function (e) {
                    e.stopPropagation();
                    var sessionId = parseInt($(this).parent().attr("data"));
                    if (allowCloseSessionDirectly) {
                        showCloseSessionDialog(sessionId);
                    } else {
                        notifyProjectEvent($CONST.ChatEvent.CHAT_SESSION_NEED_CLOSE, {
                            sessionId: sessionId,
                            session: session
                        });
                    }
                });
            }
            chatItem.on('contextmenu', function (e) {
                e.stopPropagation();
                e.preventDefault();
                var sessionId = parseInt($(this).attr("data"));
                let x = e.pageX,
                    y = e.pageY,
                    at = $('.left_panel', container);
                x = x - at.offset().left + at.scrollLeft();
                y = y - at.offset().top + at.scrollTop();
                $(this).hasClass('top') ?
                    $('.session_more_menu .stick', container).attr('title', i18n.unsticky).html('<i class="fa fa-slash slash"><i class="fa fa-arrow-to-top"></i></i>' + i18n.unsticky) :
                    $('.session_more_menu .stick', container).attr('title', i18n.sticky).html('<i class="fa fa-arrow-to-top"></i>' + i18n.sticky);
                $(this).hasClass('locked') ?
                    $('.session_more_menu .lock', container).html('<i class="fa fa-unlock"></i>' + i18n.unlock) :
                    $('.session_more_menu .lock', container).html('<i class="fa fa-lock"></i>' + i18n.lock);

                $('.session_more_menu', container).css({
                    left: x,
                    top: y
                }).data('sessionId', sessionId).show();
            });

            $('.avatar img', chatItem).on('error', function () {
                var avatar = ICONS.CLIENT.PC;
                if ($F.equalsIgnoreCase(session.client.from, 'APP')) {
                    avatar = ICONS.CLIENT.PHONE;
                } else if ($F.equalsIgnoreCase(session.client.from, 'MOBILE')) {
                    avatar = ICONS.CLIENT.PHONE;
                } else if ($F.equalsIgnoreCase(session.client.from, 'WECHAT')) {
                    avatar = ICONS.CLIENT.WECHAT;
                }
                $(this).attr('src', avatar);
            });

            if (client && client.tags) {
                var tagsContainer = $('<div class="tags">');
                $.each(client.tags, function (i, t) {
                    tagsContainer.append('<span style="background-color:' + t.bgColor + ';color:' + t.color + '">' + t.comments + '</span>');
                });
                chatItem.append(tagsContainer);
            }

            if (enableClientRating) {
                var $rateYo = $('<div class="rate">').rateYo({
                    starWidth: '20px',
                    normalFill: '#A0A0A0',
                    ratedFill: '#fab400',
                    fullStar: true,
                    numStars: 5,
                    minValue: 1,
                    maxValue: 5,
                    onSet: function (rating, rateYoInstance) {
                        callService({
                            data: {
                                requestType: "setClientRating",
                                clientId: session.clientId,
                                rating: rating
                            }
                        }).fail(function () {
                            $F.err('Chat - setClientRating', i18n.error.setClientRatingFailed);
                        });
                    }
                });
                if (session.client.rating) {
                    $rateYo.rateYo("rating", session.client.rating);
                }
                chatItem.append($rateYo);
            }

            if (sinkAfterEnds) {
                var appended = false,
                    allSessionItems = $('.session_list', container).children();
                $.each(allSessionItems, function (i, sessionItem) {
                    var sId = $(sessionItem).attr('data');
                    var s = sessionsMap["session" + sId];
                    if (s && !s.active) {
                        $(sessionItem).before(chatItem);
                        appended = true;
                        return false;
                    }
                });
                if (!appended) {
                    chatItem.appendTo($('.session_list', container));
                }
            } else {
                chatItem.appendTo($('.session_list', container));
            }
            //触发事件，会话被创建
            notifyProjectEvent($CONST.ChatEvent.CHAT_SESSION_CREATED, {
                sessionId: sessionId,
                session: session
            });
            //最后去判断是否是需要初始化坐席辅助的功能
            if (session.sessionParams && session.sessionParams.robotEngine) {
                var robotEngine = session.sessionParams.robotEngine;
                if (!Robot.isInited()) {
                    Robot.initSearch(robotEngine, function () {
                        //发送欢迎语
                        if (robotEngine.robotWelcomeMessage)
                            Robot.doSearch(sessionId, robotEngine.robotWelcomeMessage, robotEngine);
                    }, function (data, sessionIdCur) {
                        var sessionCur = sessionsMap["session" + sessionIdCur],
                            lastClientMessage = sessionCur.lastClientMessage;
                        if (lastClientMessage) {
                            var robotEngine = sessionCur.sessionParams.robotEngine;
                            var robotResponse = Robot.parseRobotMessageForAgentAssist(lastClientMessage.text, data, robotEngine, sessionCur);
                            lastClientMessage.robotResponse = robotResponse;
                            if (lastClientMessage.robotResponse) {
                                appendRobotResponse(lastClientMessage, session);
                            }
                        }
                    });
                }
            }
        }

        function refreshSessionStat() {
            var closedSessionCount = $('.session_list .chat_item.closed', container).length;
            var totalSessionCount = $('.session_list .chat_item', container).length;
            if (totalSessionCount > 0) {
                $('.header .info .nickname .name_top .session_stat', container).html('(' + (totalSessionCount - closedSessionCount) + '/' + totalSessionCount + ')');
            } else {
                $('.header .info .nickname .name_top .session_stat', container).html('&nbsp;');
            }
        }

        // 状态改变的时候，检查所改变的状态是否有预约状态，如果有并且当前存在活动会话，则先改变成预约状态
        function checkForReservingStatusWhenStatusChange(status) {
            if (status === USER_STATUS.ONLINE) {
                c.currentReservedStatusId = undefined;
                return status;
            }
            if (c.userStatus[status].reservingId) {
                var totalActiveSessionCount = $('.session_list .chat_item:not(.closed)', container).length;
                if (totalActiveSessionCount > 0) {
                    c.currentReservedStatusId = status;
                    status = c.userStatus[status].reservingId;
                    $F.log('Chat - checkForReservingStatusWhenStatusChange', 'Use reserving status ' + status + ' instead of status ' + c.currentReservedStatusId);
                }
            }
            return status;
        }

        // 当会话结束时候，检查是否已经没有活动会话，并且是否有预约的状态，如果有则改变坐席装填成预约的状态
        function checkReservedStatusWhenSessionEnd() {
            if (c.currentReservedStatusId) {
                var totalActiveSessionCount = $('.session_list .chat_item:not(.closed)', container).length;
                if (totalActiveSessionCount === 0) {
                    c.changeStatus(c.currentReservedStatusId);
                    c.currentReservedStatusId = undefined;
                }
            }
        }

        function switchTabAndSession(tabId, force) {
            $F.logGroup("chat - switchTabAndSession", "switchTabAndSession to tabId " + tabId);
            //先记住当前输入框里输入过的内容
            if (currentSessionId && sessionsMap["session" + currentSessionId]) {
                if ($('.quoteComplete', container)[0]) {
                    sessionsMap["session" + currentSessionId].quoteMessage = $('.control_panel .content .quoteComplete', container).html();
                    $('.quoteComplete', container)[0].remove()
                }
                sessionsMap["session" + currentSessionId].editingMessage = $('.control_panel .content #edit-area', container).html();
            }
            var newSessionId = tabId.split('_')[1],
                isNotCurrentSession = currentSessionId == null || newSessionId != currentSessionId;
            if (isNotCurrentSession || force) {
                //清除reddot
                $('#sessionTab_' + newSessionId + ' i.web_wechat_reddot_middle', container).remove();
                $('.session_list', container).children().removeClass('active');
                $('#sessionTab_' + newSessionId, container).addClass('active');

                var output = '',
                    preMsgUserId = '',
                    sessionCur = sessionsMap['session' + newSessionId],
                    sessionLastTime = sessionCur.active ? (new Date().getTime() - sessionCur.creationTime) : (sessionCur.endTime - sessionCur.creationTime),
                    allMessages = sessionCur.allMessages;
                $('.session_hd .title_name', container).html(sessionCur.client.name + ((sessionCur.userCount > 2) ? '(' + sessionCur.userCount + ')' : ''));
                $('.session_hd .session_last', container).html(i18n.lasting + ': ' + Tools.getFormattedLasting(sessionLastTime));
                $F.log('Chat - switchTabAndSession', 'session [' + newSessionId + '] message length ' + allMessages.length);
                $.each(allMessages, function (i, message) {
                    var displayMsg = getDisplayMessage(message, sessionCur);
                    output += displayMsg;
                    preMsgUserId = message.userId;
                });
                sessionCur.preMsgUserId = preMsgUserId;
                $('#agentResponseDiv', container).html('');
                appendTextToDiv(output);

                // 判断是否有引用消息
                if (sessionCur.quoteMessage && JSON.stringify(sessionCur.quoteMessage) != "{}") {
                    const inputArea = $('#edit-area', container)[0].parentNode
                    const quoteComplete = $('.quoteComplete', inputArea)
                    if (quoteComplete.length) {
                        quoteComplete[0].innerHTML = sessionCur.quoteMessage || ''
                    } else {
                        const quoteCompleteDiv = document.createElement('DIV')
                        quoteCompleteDiv.className = 'quoteComplete'
                        quoteCompleteDiv.innerHTML = sessionCur.quoteMessage || ''
                        inputArea.insertBefore(quoteCompleteDiv, $('#edit-area', container)[0])
                    }
                }

                $('.control_panel .content #edit-area', container).html(sessionCur.editingMessage || '');
                setTimeout(function () {
                    scrollToBottom('.message_panel');
                }, 200);
            }
            $F.log("Chat - switchTabAndSession", "set currentSessionId to " + newSessionId);
            currentSessionId = newSessionId;
            var sessionCur = sessionsMap["session" + currentSessionId];
            //设置工具栏的需要翻译的语言
            setLanguageSelect((Boolean(sessionCur.languageTo) ? transLang(sessionCur.languageTo) : transLang(sessionCur.lang)) || '');
            currentCustomerGuid = sessionCur.clientId;
            callService({
                data: {
                    requestType: "setCurrentChatSessionId",
                    sessionId: currentSessionId
                }
            }).done(function (data) {
                if (data.status === 'success') {
                    $F.log("Chat - switchTabAndSession", "setCurrentChatSessionId successful, sessionId=" + currentSessionId + "  clientId=" + sessionCur.clientId);
                }
            });

            if (sessionCur.active) {
                if (isNotCurrentSession) {
                    enableSession(newSessionId);
                    // 自动光标到输入框，并且到输入框输入内容的最后
                    $('.control_panel .content #edit-area', container).focus();
                    Tools.setEndOfContenteditable($('.control_panel .content #edit-area', container)[0]);
                }
            } else {
                disableSession(newSessionId);
            }

            //第一次进入持久会话时候，去加载历史消息
            if (sessionCur.permFlag == 1) {
                if (!sessionCur.entered) {
                    callService({
                        data: {
                            requestType: "getSessionPreviousMessages",
                            sessionId: newSessionId,
                            oldestMessageId: sessionCur.oldestMessage ? sessionCur.oldestMessage.id : 0,
                            count: countForFetchingPreviousMessage
                        },
                        success: data => {
                            if (data.status === "success") {
                                var msgs = data.msgs;
                                if (msgs && msgs.length > 0) {
                                    msgs = msgs.reverse(); //反转消息数据，消息时间从远到近
                                    $.each(msgs, function (i, message) {
                                        if (typeof message === 'string')
                                            message = JSON.parse(message);
                                        var text = tools.parseDisplayContent(message.text);
                                        if (text) {
                                            addMessageToSession(newSessionId, {
                                                id: message.userId,
                                                name: message.userName,
                                                icon: message.icon
                                            }, {
                                                text: text,
                                                history: true,
                                                translate: message.translateMessage
                                            }, Tools.getFormattedTime(message.postTime));
                                        }

                                        // 历史消息最后一条前增加历史消息标识
                                        if (i == msgs.length - 2) {
                                            addMessageToSession(newSessionId, {
                                                id: "-1",
                                                name: null
                                            }, {
                                                text: formatFeedbackMessage(i18n.feedback.hisMessagesAbove),
                                                history: true,
                                                type: MESSAGE_TYPE.SYSTEM
                                            });
                                        }
                                    });
                                    sessionCur.oldestMessage = msgs[msgs.length - 1];
                                    sessionCur.currentOldestMessage = sessionCur.oldestMessage;
                                } else {
                                    addMessageToSession(newSessionId, {
                                        id: "-1",
                                        name: null
                                    }, {
                                        text: formatFeedbackMessage(i18n.feedback.noMoreHisMessages),
                                        history: true,
                                        type: MESSAGE_TYPE.SYSTEM
                                    });
                                }
                                var output = '';
                                $.each(sessionCur.allMessages, function (i, message) {
                                    var displayMsg = getDisplayMessage(message, sessionCur);
                                    output += displayMsg;
                                    preMsgUserId = message.userId;
                                });
                                appendTextToDiv(output);
                            }
                            sessionCur.entered = true;
                        }
                    }, true);
                }
            }
            //显示预览信息
            refreshPreMessage(sessionCur.previewMessage, newSessionId);
            //发出聊天客户改变事件
            notifyProjectEvent($CONST.ChatEvent.CHAT_SESSION_CHANGED, {
                sessionId: newSessionId,
                session: sessionCur
            });
            //清除typing提示
            removeTypingNotice();
            $F.logGroupEnd();
        }

        /**
         * 设置会话相关按钮可用
         * @param sessionId
         */
        function enableSession(sessionId) {
            $('.btn_send', container).attr('disabled', false);
            $('.control_panel .content #edit-area', container).css({
                color: '#000'
            });
            $('.control_panel .content #edit-area', container).attr('contenteditable', true);
            $('.toolbar', container).css('display', 'block');
            $('.chat_face', container).css('display', 'inline-block');
            $('.chat_screencut,.chat_pic', container).css('display', uploadDisabled ? 'none' : 'inline-block');
            if (c.configs.pushRatingEnabled) {
                $('.chat_push_rating', container).css('display', 'inline-block');
                let sessionCur = sessionsMap["session" + sessionId];
                if (sessionCur.hasPushedRating) {
                    $('.chat_push_rating', container).addClass('rated');
                } else {
                    $('.chat_push_rating', container).removeClass('rated');
                }
            }
            if (Object.keys(summaryParams).length) {
                $('.chat_minimax_summary', container).css('display', 'inline-block');
            }
            if (Object.keys(optimizationParams).length) {
                $('.chat_minimax_optimization', container).css('display', 'inline-block');
            }
            if (Object.keys(aiResponseParams).length) {
                $('.chat_minimax_ai_response', container).css('display', 'inline-block');
            }
            if (c.configs.translateEngine) {
                $('.chat_translate_container', container).css('display', 'inline-block');
            }
            $('.session_hd .title_wrap .session_btn_container', container).css('display', 'inline-block');
            if (showSessionButtons === 1) {
                $('.session_hd .title_wrap .session_btn_container .invite', container).show()
                $('.session_hd .title_wrap .session_btn_container .transfer', container).show()
                $('.session_hd .title_wrap .session_btn_container .transferToQueue', container).show()
                $('#mmpop_system_menu .dropdown_menu .invite_li', container).show()
                $('#mmpop_system_menu .dropdown_menu .transfer_li', container).show()
                $('#mmpop_system_menu .dropdown_menu .transferToQueue_li', container).show()
            } else if (typeof showSessionButtons === "object" && showSessionButtons !== null) {
                if (showSessionButtons.isInviteShow) {
                    $('.session_hd .title_wrap .session_btn_container .invite', container).show()
                    $('#mmpop_system_menu .dropdown_menu .invite_li', container).show()
                } else {
                    $('.session_hd .title_wrap .session_btn_container .invite', container).hide()
                    $('#mmpop_system_menu .dropdown_menu .invite_li', container).hide()
                }
                if (showSessionButtons.isTransferShow) {
                    $('.session_hd .title_wrap .session_btn_container .transfer', container).show()
                    $('#mmpop_system_menu .dropdown_menu .transfer_li', container).show()
                } else {
                    $('.session_hd .title_wrap .session_btn_container .transfer', container).hide()
                    $('#mmpop_system_menu .dropdown_menu .transfer_li', container).hide()
                }
                if (showSessionButtons.isTransferToQueueShow) {
                    $('.session_hd .title_wrap .session_btn_container .transferToQueue', container).show()
                    $('#mmpop_system_menu .dropdown_menu .transferToQueue_li', container).show()
                } else {
                    $('.session_hd .title_wrap .session_btn_container .transferToQueue', container).hide()
                    $('#mmpop_system_menu .dropdown_menu .transferToQueue_li', container).hide()
                }
            } else {
                $('.session_hd .title_wrap .session_btn_container .invite', container).hide()
                $('.session_hd .title_wrap .session_btn_container .transfer', container).hide()
                $('.session_hd .title_wrap .session_btn_container .transferToQueue', container).hide()
                $('#mmpop_system_menu .dropdown_menu .invite_li', container).hide()
                $('#mmpop_system_menu .dropdown_menu .transfer_li', container).hide()
                $('#mmpop_system_menu .dropdown_menu .transferToQueue_li', container).hide()
            }
            $('#sessionTab_' + sessionId, container).removeClass('closed');
        }

        /**
         * 设置会话相关按钮不可用
         * @param sessionId
         */
        function disableSession(sessionId) {
            if (sessionId == currentSessionId) {
                $('.btn_send', container).attr('disabled', true);
                var session = sessionsMap['session' + sessionId];
                if (session) {
                    //会话结束时候，根据客户id来缓存当时编辑的消息
                    clientCache[session.clientId] = {
                        editMessage: $('.control_panel .content #edit-area', container).html()
                    }
                    $('.control_panel .content #edit-area', container).html('')
                }
                $('.control_panel .content #edit-area', container).css({
                    color: '#b8b8b9'
                });
                $('.control_panel .content #edit-area', container).attr('contenteditable', false);
                refreshPreMessage();
                $('.toolbar', container).css("display", "none");
                $('.chat_face,.chat_screencut,.chat_pic,.chat_push_rating,.chat_translate_container', container).css('display', 'none');
                $('.session_hd .title_wrap .session_btn_container', container).css('display', 'none');
            }
            $('#sessionTab_' + sessionId, container).addClass('closed');
        }

        /**
         * 删除左侧会话标签
         * @param sessionId
         */
        function removeSessionItem(sessionId) {
            $('#sessionTab_' + sessionId, container).remove();
            delete sessionsMap["session" + sessionId];
        }

        /**
         * 设置右侧想干按钮不可用，同时删除左侧会话对应的标签
         * @param sessionId
         */
        function closeSession(sessionId) {
            disableSession(sessionId);
            removeSessionItem(sessionId);
        }

        function getDisplayMessage(message, session) {
            if (!message.displayMessage) {
                var userId = message.userId,
                    stateId = message.stateId || '',
                    userName = encodeStringByHTMLEscape(message.userName),
                    translate = message.translate,
                    msgText = message.text,
                    // msgText = (typeof message.text === 'string' && message.text.startsWith('&lt;[img]')) ? message.text.replace('&lt;[img]', '<img').replace('&gt;', '>') : message.text,
                    formattedTime = message.formattedTime,
                    extra = message.extra || '',
                    extraContent = '',
                    monitorSender = message.monitorSender || '',
                    quoteMessage = message.quoteMessage ? {
                        text: message.quoteMessage.text,
                        id: message.quoteMessage.id,
                        user: message.quoteMessage.user
                    } : undefined,
                    displayMsg;
                if (extra) {
                    if (extra.Content) {
                        if (i18n.messageExtra)
                            extraContent = '<p class="js_extra_title">' + i18n.messageExtra + '</p>';
                        extraContent += '<pre class="js_extra_plain">' + extra.Content + '</pre>';
                    }
                }
                if (!msgText) {
                    $F.log('chat - getDisplayMessage', 'Empty message: ' + JSON.stringify(message));
                    return '';
                }
                if (userId == "-1") {
                    if (msgText.indexOf('[videoMeet]') !== -1) {
                        msgText = msgText.replace('[videoMeet]', '<a class="fonticon" data-ref="[l]video"><i class="fal fa-video" style="font-size: 18px;"></i></a>')
                    }
                    //过滤表情字符串，把[表情]转换成表情对应的img标签
                    msgText = EMOTION.filterEmojis(msgText);
                    //把url字符串包装成a标签的连接
                    msgText = Tools.wrapURL(msgText, CHAWUR);

                    displayMsg = msgText;
                } else {
                    if (msgText.indexOf('[videoMeet]') !== -1) {
                        msgText = msgText.replace('[videoMeet]', '<a class="fonticon" data-ref="[l]video"><i class="fal fa-video" style="font-size: 18px;"></i></a>')
                    }
                    //解析某种格式化消息
                    msgText = Tools.parseFormattedMsg(msgText);
                    //过滤表情字符串，把[表情]转换成表情对应的img标签
                    msgText = EMOTION.filterEmojis(msgText);
                    //把url字符串包装成a标签的连接
                    msgText = Tools.wrapURL(msgText, CHAWUR);
                    // 判断消息是否需要可以翻译，根据消息是不是有文本来判断
                    let needTranslate = Tools.isNeedTranslate(msgText);

                    var avatar;
                    if (userId === c.configs.agentId) {
                        avatar = c.configs.agentHeadImg ? (headImgBaseUrl + c.configs.agentHeadImg) : ICONS.AGENT.ELITE;
                        if (translate) {
                            let messageMiddleText = EMOTION.filterEmojis(translate);
                            translate = i18n.originalText + msgText;
                            msgText = messageMiddleText;
                        }
                    } else if (userId === session.clientId) {
                        avatar = session.client.icon;
                        if (translate)
                            translate = i18n.translationResult + EMOTION.filterEmojis(translate);
                    } else {
                        if (Robot.isRobot(userId)) {
                            var sessionParams = session.sessionParams;
                            var rootIcon = '';
                            if (sessionParams && sessionParams.robotEngine && sessionParams.robotEngine.icon) {
                                rootIcon = sessionParams.robotEngine.icon;
                            }
                            avatar = message.icon || rootIcon || ICONS.AGENT.ROBOT;
                        } else {
                            var user = session.users[userId];
                            if (translate)
                                translate = i18n.translationResult + EMOTION.filterEmojis(translate);
                            if (user) {
                                avatar = user.headImg ? (headImgBaseUrl + user.headImg) : ICONS.AGENT.OTHER_AGENT;
                            } else {
                                avatar = message.icon ? (headImgBaseUrl + message.icon) : ICONS.AGENT.OTHER_AGENT;
                            }
                        }
                    }
                    displayMsg = '<div class="clearfix" data-state="' + stateId + '">' +
                        '<div class="message ' + fetchMessageClass(userId, session) + ' ' +
                        (needTranslate ? '' : 'no-translate') + ' ' +
                        (translate ? 'translated' : '') + '" data-user="' + userId + '" data-session="' + session.id + '">' +
                        '<img class="avatar" src="' + avatar + '" >' +
                        '<div class="content">' +
                        '<h4 class="nickname">' + '<span class="name" ' + (userId !== c.configs.agentId ? '' : 'style="display: none"') + '>' + userName + '</span><span>' + (monitorSender !== '' ? '[' + i18n.agent + ']' : '') + '</span> <span class="time">' + formattedTime + '</span></h4>' +
                        ((message.revokeFlag && userId === c.configs.agentId) ? '<div class="revoked">' + i18n.revoked + '<i class="reedit fas fa-info-circle" title="' + i18n.reedit + '"></i></div>' : '') +
                        (message.stateId ? '<i class="failed fa fa-exclamation-circle" style="display: none"></i>' : '') +
                        '<div class="bubble js_message_bubble ' + fetchMessageBubbleClass(userId, session) + '">' +
                        '<div class="bubble_cont' + ' ' + (message.revokable ? 'ns' : '') + '">' +
                        '<div class="plain">' +
                        (message.quoteMessage ? ('<div class="quote-message-content" quote-message-id="' + message.quoteMessage.id + '"><span class="rows-ellipsis">' + message.quoteMessage.user + ': ' + message.quoteMessage.text + '</span></div>') : '') +
                        '<pre class="js_message_plain" data-id="' + (message.newMessageId || message.revokeId || message.id) + '">' + msgText + '</pre>' + extraContent +
                        '</div>' +
                        '</div>' +
                        (needTranslate ? ('<div class="translate-message  ' + fetchMessageBubbleClass(userId, session) + (!translate ? ' translate-hide' : '') + '">' +
                            '<div class="plain">' +
                            '<pre class="js_message_plain" data-id="' + message.id + '">' + translate + '</pre>' +
                            '</div>' +
                            '</div>') : '') +
                        '</div>' +
                        ((message.revokeFlag && userId !== c.configs.agentId) ? '<div class="revoked">' + i18n.revoked + '</div>' : '') +
                        (userId == c.configs.agentId && message.read ? '<p class="has-read has-read-' + message.readMessageId + '">' + message.read + '</p>' : '') +
                        '</div>' +
                        '</div>' +
                        '</div>';
                }
                message.displayMessage = displayMsg;

                if (message.robotResponse) { // 如果消息有机器人答复，则需要拼接上相关答案
                    var displayMessageContainer = $('<div>').append(message.displayMessage);
                    $('.js_message_plain', displayMessageContainer).html(message.robotResponse);
                    message.displayMessage = displayMessageContainer.html();
                }

            }
            return message.displayMessage;
        }

        function fetchMessageClass(userId, session) {
            if (messageBubbleStyle === 'dc') { // 优先区别出客户，即所有客户的消息在左侧，其他消息都在右侧
                return userId === session.clientId ? '' : 'me';
            }
            return userId === c.configs.agentId ? 'me' : ''; // 优先区别出当前坐席，即所有当前坐席的消息在左侧，其他消息都在右侧（默认）
        }

        function fetchMessageBubbleClass(userId, session) {
            if (messageBubbleStyle === 'dc') { // 优先区别出客户，即所有客户的消息在左侧，其他消息都在右侧
                return userId === session.clientId ? 'bubble_default left' : 'bubble_primary right';
            }
            return userId === c.configs.agentId ? 'bubble_primary right' : 'bubble_default left'
        }

        function formatFeedbackMessage(msg) {
            return '<p class="message_system">' + msg + '</p>';
        }

        function appendTextToDiv(content, containerId = 'agentResponseDiv') {
            var target;
            try {
                target = $(content);
            } catch (e) { }
            if (!target || target.length === 0) {
                target = $('<div>' + content + '</div>');
            }

            // 包装消息，绑定各种消息事件
            wrapMessage(target, containerId);

            var new_Len = target.length;
            var cur_Len = $('#' + containerId, container)[0].childNodes.length;
            console.log('最新消息数量:' + new_Len + ' 已展示数量:' + cur_Len);
            console.log($('#' + containerId, container)[0].childNodes);

            for (let i = 0; i < new_Len; i++) {
                var new_Text = target[i].textContent;
                console.log('新消息(' + i + '):', new_Text);

                // 检索已展示消息中是否包含
                for (let j = 0; j < cur_Len; j++) {
                    var cur_Text = $('#' + containerId, container)[0].childNodes[j].textContent;

                    // 判断当前消息是否和新消息一样
                    if (cur_Text == new_Text) {
                        console.log('重复消息(' + i + '):', cur_Text);
                        // target[i].style.display = 'none';
                        $('#' + containerId, container)[0].childNodes[j].style.display = 'none';
                    }
                }
            }

            $('#' + containerId, container).append(target);
            scrollToBottom('.message_panel');

        }

        /**
         * 包装消息，绑定各种消息事件
         * @param containerId agentResponseDiv
         * @param target 消息的dom对象
         */
        function wrapMessage(target, containerId = 'agentResponseDiv') {
            //给图片添加点击事件 放大图片显示
            $('img:not(.avatar,.qqemoji,.standard-emoji,.location,.custom-emoji,div.message-imgtext .thumb,div.message-card .card-img)', target).one('load', function () {
                $(this).css("cursor", "pointer").on("click", function () {
                    Tools.showOriginImage(this, chatServerAddr, $('img:not(.avatar,.qqemoji,.standard-emoji,.location,.custom-emoji)', $('#' + containerId)));
                });
                Tools.autoResizeImage(300, 300, this);
                scrollToBottom('.message_panel');
            });
            //给voice添加点击事件，播放语音
            $('div.message-voice', target).off().on('click', function () {
                var $voice = $(this);
                $('div.message-voice').removeClass('active');
                var url = $voice.attr('data-url');
                if (url) {
                    if (currentVoiceAudio && !currentVoiceAudio.paused && !currentVoiceAudio.ended && 0 < currentVoiceAudio.currentTime) {
                        currentVoiceAudio.pause();
                        currentVoiceAudio.currentTime = 0;
                        $voice.removeClass('active');
                        return;
                    }
                    if (!$voice.hasClass('listened')) {
                        $voice.addClass('listened');
                    }
                    if (!url.toLowerCase().startsWith("http")) {
                        url = chatServerHost + url;
                    }
                    currentVoiceAudio = new Audio(url);
                    $(currentVoiceAudio).on('play', function () {
                        $voice.addClass('active');
                    }).on('ended', function () {
                        $voice.removeClass('active');
                    }).on('error', function () {
                        $voice.removeClass('active');
                    });
                    currentVoiceAudio.play();
                }
            });
            $('div.message-file', target).off().on('click', function () {
                var $file = $(this);
                var url = $file.attr('data-url');
                if (url) {
                    if (!url.toLowerCase().startsWith("http")) {
                        url = chatServerHost + url;
                    }
                    window.open(url, 'FILE_DOWNLOAD');
                }
            });
            $('div.message-location', target).off().on('click', function () {
                var $location = $(this);
                var latitude = $location.attr('data-latitude'),
                    longitude = $location.attr('data-longitude'),
                    label = $location.attr('data-address'),
                    map = $location.attr('data-map'),
                    mapUrl = 'aMaps.html';
                if (map === 'baidu') {
                    mapUrl = 'baiduMaps.html'
                } else if (map === 'tencent') {
                    mapUrl = 'tencentMaps.html'
                }
                window.open(chatServerAddr + '/' + mapUrl + '?latitude=' + latitude + '&longitude=' + longitude + '&label=' + encodeURIComponent(label), '_blank');
            });
            $('div.message-imgtext, div.message-card', target).off().on('click', function () {
                const url = new URL($(this).attr('data-url'))
                url.searchParams.append('open-from', 'agent');
                var agentMute = $(this).attr('data-agent-mute');
                if (url && !agentMute)
                    window.open(url.href, '_blank');
            });
            $('video', target).each(function (i, video) {
                var $video = $(video),
                    src = $video.attr('src') || $('source', $video).attr('src');
                if (src) {
                    if (!src.toLowerCase().startsWith('http')) {
                        src = chatServerHost + src;
                    }
                    $video.attr('src') ? $video.attr('src', src) : $('source', $video).attr('src', src);
                    $video.css('width', 300).on('canplay', function () {
                        scrollToBottom('.message_panel');
                    });
                    //退出全屏会自动滚到最顶部，没法记住原来滚动到的位置，目前只能退出全屏时候滚到最下面
                    $video.on('webkitfullscreenchange', function () {
                        var isFullscreenNow = document.webkitFullscreenElement !== null;
                        if (isFullscreenNow) {

                        } else {
                            scrollToBottom('.message_panel');
                        }
                    });
                }
            });
            $('.message', target).each(function (i, msg) {
                var userId = $(this).attr('data-user'),
                    sessionId = $(this).attr('data-session');
                $('.avatar', this).off().on('error', function () {
                    var avatar, session = sessionsMap["session" + sessionId];
                    if (userId === c.configs.agentId) {
                        avatar = ICONS.AGENT.ELITE
                    } else if (userId === session.clientId) {
                        if ($F.equalsIgnoreCase(session.client.from, 'APP')) {
                            avatar = ICONS.CLIENT.PHONE;
                        } else if ($F.equalsIgnoreCase(session.client.from, 'MOBILE')) {
                            avatar = ICONS.CLIENT.PHONE;
                        } else if ($F.equalsIgnoreCase(session.client.from, 'WECHAT')) {
                            avatar = ICONS.CLIENT.WECHAT;
                        } else {
                            avatar = ICONS.CLIENT.PC;
                        }
                    } else {
                        avatar = ICONS.AGENT.OTHER_AGENT;
                    }
                    $(this).attr('src', avatar);
                });
            });
            $('.quote-message-content', target).off().on('click', function (e) {
                const quoteId = e.target.parentNode.closest('.quote-message-content').getAttribute('quote-message-id')
                let MessageDom = null
                try {
                    MessageDom = $('[data-id="' + quoteId + '"].js_message_plain')[0].parentElement.closest('.message')
                } catch (e) { }
                if (MessageDom) {
                    MessageDom.style.background = 'rgba(0, 0, 0, 0.03)'
                    MessageDom.scrollIntoView({
                        behavior: 'smooth'
                    })
                    setTimeout(() => {
                        MessageDom.style.background = ''
                    }, 1500)
                } else {
                    let sessionId = 0
                    try {
                        sessionId = e.target.parentElement.closest('.message').getAttribute('data-session') || 0
                    } catch (e) { }
                    if (sessionId) {
                        $F.err('引用内容不在当前历史会话中');
                        // const session = sessionsMap["session" + sessionId];
                        // callService({
                        //     data: {
                        //         requestType: 'positioningMessages',
                        //         clientId: session.clientId,
                        //         messageId: quoteId,
                        //         queue: session.queueId
                        //     }
                        // }).done(function (data) {
                        //     if (data.status === 'success') {
                        //         console.log(data)
                        //     } else {
                        //         $F.err('引用内容不存在');
                        //     }
                        // })
                    } else {
                        $F.err('引用内容不存在');
                    }
                }
            });

            if (c.configs.translateEngine || messageQuoteEnabled || messageRevokeEnabled) {
                const contextMenuParams = {
                    items: {},
                    myItems: {},
                    onRevoke: function (key, options, target) {
                        if (key === 'revoke') {
                            var messageId = $('.js_message_plain', target).attr('data-id');
                            var sessionId = $(target).parent().parent().parent().attr('data-session');
                            callService({
                                data: {
                                    requestType: 'send',
                                    data: getPostNoticeMessageSentInfo(messageId, sessionId, NOTICE_TYPE.REVOKE_MESSAGE)
                                }
                            }).done(function (data) {
                                if (data.status === 'success' && data.sendResponse && data.sendResponse.result == 1) {
                                    $F.notify(i18n.revokedSuccess);
                                    //删除这条消息
                                    target.removeClass('ns');
                                    $('<div class="revoked">' + i18n.revoked + '<i class="reedit fas fa-info-circle" title="' + i18n.reedit + '"></i></div>').insertBefore(target.parent());
                                    $('.reedit', target.parent().parent()).on('click', function () {
                                        reedit($(this).parent().parent(), container);
                                    });
                                    const sessionCur = sessionsMap["session" + sessionId];
                                    if (sessionCur && sessionCur.allMessages) {
                                        sessionCur.allMessages.forEach(function (message) {
                                            if (message.newMessageId == messageId) {
                                                message.revokeFlag = 1;
                                                message.revokable = false;
                                                // 清空这条消息显示缓存
                                                message.displayMessage = undefined;
                                            }
                                            if (message.userId.length === 36) {
                                                setUserInWaiting(sessionCur, true);
                                            } else {
                                                if (message.type === MESSAGE_TYPE.NORMAL && message.revokeFlag !== 1) {
                                                    setUserInWaiting(sessionCur, false);
                                                }
                                            }
                                        });
                                    }
                                } else {
                                    $F.alert(i18n.error.revokeMessageFailed + (data.sendResponse.result === -37 ? ': 消息超时' : ''));
                                }
                            }).fail(function () {
                                $F.err('Chat - messageRevoke', i18n.error.revokeMessageFailed);
                            });
                        }
                    },
                    onTranslate: function (key, options, target) {
                        if (key === 'translate') {
                            if ($(target).hasClass('translated'))
                                return;
                            var content = $('.js_message_plain', target).html(),
                                messageId = $('.js_message_plain', target).data("id");
                            callService({
                                data: {
                                    requestType: 'translate',
                                    text: content,
                                    messageId: messageId,
                                    targetLang: $.cookie('chat.translateTo') || 'zh'
                                }
                            }).done(function (data) {
                                if (data.status === "success") {
                                    $(target).addClass('translated');
                                    $('.bubble_cont', target)
                                        .after('<div class="translate-message">' +
                                            '<div class="plain">' +
                                            '<pre class="js_message_plain" data-id="">' + i18n.translationResult + data.text + '</pre>' +
                                            '</div>' +
                                            '</div>');
                                } else {
                                    $F.err('Chat - translate', i18n.error.translateFailed + ': ' + data.message);
                                }
                            }).fail(function (e) {
                                $F.err('Chat - translate', i18n.error.translateFailed);
                            });
                        }
                    },
                    onQuote: function (key, options, target) {
                        if (key === 'quote') {
                            const inputArea = $('#edit-area', container)[0].parentNode
                            const quoteText = $('.js_message_plain', target)[0].innerHTML
                            const quoteUserName = $('.nickname .name', target[0].parentNode.parentNode.parentNode)[0].innerHTML
                            const newMessageId = $('.js_message_plain', target)[0].getAttribute('data-id')
                            const newInnerHTML =
                                '<div class="quote-complete-content" new-msg-id="' + newMessageId + '"><div>' +
                                '<span class="quote-complete-user">' + quoteUserName + '</span>' + '<span>: </span>' + '<div class="quote-complete-text rows-ellipsis">' +
                                quoteText + '</div>' + '</div><div class="quote-complete-clean-btn"><i class="fa fa-times-circle"></i></div></div>'
                            const quoteComplete = $('.quoteComplete', inputArea)
                            const quoteCompleteDiv = document.createElement('DIV')
                            quoteCompleteDiv.className = 'quoteComplete'
                            quoteCompleteDiv.innerHTML = newInnerHTML
                            if (quoteComplete.length) {
                                quoteComplete[0].innerHTML = newInnerHTML
                            } else {
                                inputArea.insertBefore(quoteCompleteDiv, $('#edit-area', container)[0])
                            }
                            if ($('.quote-complete-clean-btn', inputArea)[0]) {
                                $('.quote-complete-clean-btn', inputArea)[0].onclick = function () {
                                    if ($('.quoteComplete', container)[0]) {
                                        $('.quoteComplete', container)[0].remove()
                                    }
                                }
                            }

                            // 自动光标到输入框，并且到输入框输入内容的最后
                            $('.control_panel .content #edit-area', container).focus();
                        }
                    }
                }
                if (c.configs.translateEngine) {
                    contextMenuParams.items['translate'] = {
                        name: i18n.translate,
                        icon: "language"
                    }
                }
                if (messageQuoteEnabled) {
                    contextMenuParams.myItems['quote'] = {
                        name: i18n.quote,
                        icon: "quote-left"
                    }
                    contextMenuParams.items['quote'] = {
                        name: i18n.quote,
                        icon: "quote-left"
                    }
                }
                if (messageRevokeEnabled) {
                    contextMenuParams.myItems['revoke'] = {
                        name: i18n.revoke,
                        icon: "undo"
                    }
                }

                if (c.configs.translateEngine) {
                    $.contextMenu({
                        selector: '.chat .message:not(.translated,.no-translate,.me) .plain',
                        callback: function (key, options, target) {
                            contextMenuParams.onTranslate(key, options, target)
                            contextMenuParams.onQuote(key, options, target)
                        },
                        iconStyle: 'fa',
                        items: contextMenuParams.items,
                        appendTo: container
                    });
                }
                if (messageQuoteEnabled) {
                    // 坐席已撤回消息
                    $.contextMenu({
                        selector: '.chat .message.me .bubble_cont:not(.ns)',
                        callback: function (key, options, target) {
                            contextMenuParams.onQuote(key, options, target)
                        },
                        iconStyle: 'fa',
                        items: {
                            quote: {
                                name: i18n.quote,
                                icon: "quote-left"
                            }
                        },
                        appendTo: container
                    });

                    // 客户发送消息
                    $.contextMenu({
                        selector: '.chat .message:not(.me).no-translate .plain,.chat .message:not(.me).translated .plain,.chat .message:not(.me) .bubble_cont .plain',
                        callback: function (key, options, target) {
                            contextMenuParams.onQuote(key, options, target)
                        },
                        iconStyle: 'fa',
                        items: {
                            quote: {
                                name: i18n.quote,
                                icon: "quote-left"
                            }
                        },
                        appendTo: container
                    });
                }
                // 坐席消息
                $.contextMenu({
                    selector: '.chat .message.me .bubble_cont.ns',
                    callback: function (key, options, target) {
                        contextMenuParams.onQuote(key, options, target)
                        contextMenuParams.onRevoke(key, options, target)
                    },
                    iconStyle: 'fa',
                    items: contextMenuParams.myItems,
                    appendTo: container
                });
            }

            $('.message .js_message_plain', target).off().on('click', function () {
                var content = $(this).html();
                notifyProjectEvent($CONST.ChatEvent.CHAT_MESSAGE_CLICKED || 'CHAT_MESSAGE_CLICKED', {
                    sessionId: currentSessionId,
                    message: content,
                    customerGuid: currentCustomerGuid
                });
            });

            //坐席辅助信息点击处理
            Robot.wrapResponse($('.robotResponseWrapper', target), function (answer) {
                let curSession = sessionsMap["session" + currentSessionId];
                if (curSession && curSession.active) {
                    $('.control_panel .content #edit-area', container).html(answer);
                }
            });

            // 撤回后，重新编辑按钮
            $('.reedit', target).off().on('click', function () {
                reedit($(this).parent().parent(), container);
            });
        }

        function scrollToBottom(selector) {
            var target = $(selector, container)[0];
            if (target) {
                target.scrollTop = target.scrollHeight;
            }
        }

        function reedit(messageContainer, container) {
            let curSession = sessionsMap["session" + currentSessionId];
            if (curSession && curSession.active) {
                let content;
                const translateMessageDom = $('.translate-message .js_message_plain', messageContainer);
                if (translateMessageDom.length > 0 && translateMessageDom.html()) {
                    content = translateMessageDom.html().substring(3); // 去掉 “原文：”
                } else {
                    content = $('.js_message_plain', messageContainer).html();
                }
                let sendContent = ''
                if ($('.message-image', messageContainer).length) {
                    const imageName = $('.message-image', messageContainer)[0].getAttribute('title') || ''
                    const imageSrc = $('.message-image', messageContainer)[0].getAttribute('src') || ''
                    sendContent = '<img data-from="elite" src = "' + imageSrc + '" title = "' + imageName + '">';
                    sendMessage(sendContent, currentSessionId, content);
                } else if ($('.message-video', messageContainer).length) {
                    const videoName = $('.message-video', messageContainer)[0].getAttribute('data-name') || ''
                    const videoSrc = $('.message-video', messageContainer)[0].getAttribute('data-url') || ''
                    const videoSize = $('.message-video', messageContainer)[0].getAttribute('data-size') || ''
                    sendContent = '<video src="' + videoSrc + '" data-name="' + videoName + '" data-size="' + videoSize + '" controls></video>';
                    sendMessage(sendContent, currentSessionId, content);
                } else if ($('.message-file', messageContainer).length) {
                    const fileName = $('.message-file', messageContainer)[0].getAttribute('data-name') || ''
                    const fileSrc = $('.message-file', messageContainer)[0].getAttribute('data-href') || ''
                    const fileSize = $('.message-file', messageContainer)[0].getAttribute('data-size') || ''
                    sendContent = '<a data-from="elite" href = "' + fileSrc + '" target = "_blank" class="attachment" data-size="' + fileSize + '">' + fileName + '</a>';
                    sendMessage(sendContent, currentSessionId, content);
                } else {
                    $('.control_panel .content #edit-area', container).append(content);
                }
            }
        }

        /**
         * 封装要发送出去的消息的json数据
         * @param message 消息正文
         * @param sessionId 会话id
         * @param messageType 消息类型，默认就是普通消息， 2表示系统自动发送的消息
         * @param extra 消息的附加信息
         */
        function getPostMessageSentInfoJSONWithSessionId(message, sessionId, messageType, extra, minimax = {}, other = {}) {
            var sentInfo = {
                senderId: c.configs.agentId,
                actionType: 'postMessage',
                sessionId: sessionId,
                message: message,
                postTime: new Date($F.getDbTime()).getTime()
            };
            if (typeof minimax.aiText !== 'undefined' && typeof minimax.aiUseState !== 'undefined') {
                if (typeof minimax.aiText !== 'string') {
                    sentInfo.aiText = JSON.stringify(minimax.aiText);
                } else {
                    sentInfo.aiText = minimax.aiText;
                }
                sentInfo.aiUseState = minimax.aiUseState;
            }
            if (messageType) {
                sentInfo.messageType = messageType;
            }
            if (extra) {
                sentInfo.extra = JSON.stringify(extra);
            }
            if (Object.keys(other).length) {
                if (other.reference) {
                    sentInfo.reference = JSON.stringify(other.reference)
                }
            }
            var sentInfoStr = JSON.stringify(sentInfo);
            $F.log('Chat - getPostMessageSentInfoJSONWithSessionId', sentInfoStr);
            return sentInfoStr;
        }

        function getPostNoticeMessageSentInfo(message, sessionId, noticeType, receiverId) {
            var sentInfo = {
                senderId: c.configs.agentId,
                actionType: "postNoticeMessage",
                sessionId: sessionId,
                noticeType: noticeType,
                message: message,
                receiverId: receiverId
            };
            return JSON.stringify(sentInfo);
        }

        /**
         * 发送消息
         * @param content 用来发送出去的消息内容
         * @param sessionId 会话id
         * @param [contentToDisplay] 用来当前显示到自己的界面的消息内容
         * @param [extra] 消息附加信息
         * @param [success] 成功回调
         * @param [fail] 失败回调
         */
        function sendMessage(content, sessionId, contentToDisplay, extra, success, fail) {
            sessionId = sessionId || currentSessionId;
            content = tools.parseContent(content);
            contentToDisplay = contentToDisplay || content;
            contentToDisplay = tools.parseDisplayContent(contentToDisplay);
            const other = {}
            if (c.highlightWords) {
                c.highlightWords.forEach(function (hw) {
                    var hwReg = new RegExp(hw, 'g');
                    contentToDisplay = contentToDisplay.replace(hwReg, '<span class="dw-highlight">' + hw + '</span>');
                });
            }
            // contentToDisplay = Tools.cottrollerFILE(contentToDisplay);
            // 用来刷新最后回复时间，确保标红能够正确消除
            const thisSession = sessionsMap["session" + sessionId];
            thisSession.lastUserMessageReceivedTime = new Date().getTime();
            // TODO: 调用接口之前，先显示要发送的消息
            const quoteMsg = {
                text: $('.quote-complete-text', container)[0] ? $('.quote-complete-text', container)[0].innerHTML : undefined,
                id: $('.quote-complete-content', container)[0] ? $('.quote-complete-content', container)[0].getAttribute('new-msg-id') : undefined,
                user: $('.quote-complete-user', container)[0] ? $('.quote-complete-user', container)[0].innerHTML : undefined
            }
            if (quoteMsg.id && quoteMsg.text) {
                const innerContent = document.createElement('DIV');
                innerContent.innerHTML = quoteMsg.text
                try {
                    if (innerContent.childNodes.length === 1) {
                        switch (innerContent.childNodes[0].nodeName) {
                            case '#text':
                                break;
                            case 'IMG':
                                quoteMsg.text = '[图片消息]'
                                break;
                            case 'VIDEO':
                                quoteMsg.text = '[视频消息]'
                                break;
                            default:
                                quoteMsg.text = '[其他消息]'
                                break;
                        }
                    } else {
                        quoteMsg.text = '[其他消息]'
                    }
                } catch (e) {
                    console.log('$(quoteMsg.text)[0].nodeName', e)
                }
            }
            let clientMessageId = '_message-' + new Date().getTime(),
                session = sessionsMap["session" + sessionId],
                message = addMessageToSession(sessionId, {
                    id: c.configs.agentId,
                    name: c.staff.displayName
                }, {
                    stateId: clientMessageId,
                    read: i18n.message.unRead,
                    text: contentToDisplay,
                    translate: false,
                    quoteMessageText: quoteMsg.text,
                    quoteMessageId: quoteMsg.id,
                    quoteMessageSender: quoteMsg.user
                }, Tools.getFormattedNowTime(true));
            message.revokable = true;
            if (currentSessionId == sessionId) {
                appendTextToDiv(getDisplayMessage(message, session));
            }
            var $domMessage = '.chat .clearfix[data-state=' + clientMessageId + ']';
            let minimaxObject = {}
            if (thisSession.lastMinimaxMessage) {
                minimaxObject.aiText = JSON.stringify(thisSession.lastMinimaxMessage);
                minimaxObject.aiUseState = Number(thisSession.lastMinimaxMessage === message.text);
            }
            if (message.quoteMessage && message.quoteMessage.id) {
                other.reference = {
                    content: {
                        displayText: message.quoteMessage.text,
                        text: message.quoteMessage.text
                    },
                    _id: message.quoteMessage.id,
                    user: {
                        name: message.quoteMessage.user
                    }
                }
            }
            callService({
                data: {
                    requestType: "send",
                    data: getPostMessageSentInfoJSONWithSessionId(content, sessionId, 0, extra, minimaxObject, other)
                }
            }).done(function (data) {
                if (data.status === 'success') {
                    if (-39 === data.sendResponse.result) {
                        if ($.isFunction(fail)) {
                            fail(data, $domMessage);
                        }
                    } else {
                        const sendResponse = data.sendResponse;
                        var messageId = sendResponse ? sendResponse.newMessageId : ('i' + _messageId++);
                        let translateMessage = data.sendResponse ? data.sendResponse.translateMessage : '';
                        showMessageOnItem(sessionId, content);
                        var session = sessionsMap["session" + sessionId];
                        session.preMsgUserId = c.configs.agentId;
                        setUserInWaiting(session, false);
                        session.lastAgentMessageSentTime = new Date().getTime();
                        if (!session.firstReply) {
                            session.firstReply = true;
                            // 坐席第一次回复消息时候触发此消息
                            notifyProjectEvent($CONST.ChatEvent.CHAT_SESSION_FIRST_REPLY || 'CHAT_SESSION_FIRST_REPLY', {
                                sessionId: sessionId
                            });
                        }
                        //通知坐席发送消息
                        notifyProjectEvent($CONST.ChatEvent.AGENT_SEND_MESSAGE || 'AGENT_SEND_MESSAGE', {
                            sessionId: sessionId,
                            message: message
                        });
                        if (currentSessionId === sessionId) {
                            // $('.chat .clearfix[data-state=' + clientMessageId + ']').remove();
                            $(".js_message_plain", $domMessage).attr('data-id', messageId);
                            $(".has-read", $domMessage).addClass('has-read-' + messageId);
                            let sessionCur = sessionsMap["session" + sessionId];
                            if (!sessionCur.sessionType) {
                                $(".has-read-" + messageId, $domMessage).remove();
                            } else if (sessionCur.sessionType !== 9 && sessionCur.sessionType !== 3) {
                                $(".has-read-" + messageId, $domMessage).remove();
                            }
                            if (translateMessage && !$('.no-translate', $domMessage)[0]) {
                                $('.translate-message', $domMessage).removeClass("translate-hide");
                                $(".js_message_plain", $('.translate-message', $domMessage)).html(i18n.originalText + contentToDisplay);
                                $(".js_message_plain", $('.bubble_cont', $domMessage)).html(i18n.translationResult + EMOTION.filterEmojis(translateMessage));
                                Tools.addImageClick($domMessage, chatServerAddr, function () {
                                    scrollToBottom('.message_panel');
                                })
                            }

                            sessionCur.allMessages.forEach(function (message) {
                                if (message.stateId === clientMessageId) {
                                    message.translate = translateMessage;
                                    message.displayMessage = '';
                                    // FIXME
                                    message.newMessageId = sendResponse.newMessageId;
                                    getDisplayMessage(message, sessionCur);
                                }
                            })


                            // var displayMsg = getDisplayMessage(message, session);
                            // appendTextToDiv(displayMsg);
                        }
                    }
                    if ($.isFunction(success)) {
                        success(data);
                    }
                } else {
                    $F.err('Chat - sendMessage', i18n.error.sendMessageFailed);
                    if ($.isFunction(fail)) {
                        fail(data, $domMessage);
                    }
                }
            }).fail(function () {
                $F.err('Chat - sendMessage', i18n.error.sendMessageFailed);
                if ($.isFunction(fail)) {
                    fail(null, $domMessage);
                }
            })
            if ($('.quoteComplete', container)[0]) {
                $('.quoteComplete', container)[0].remove();
                thisSession.quoteMessage = '';
            }
            thisSession.lastMinimaxMessage = ''
        }

        function encodeStringByHTMLEscape(str) {
            if (!str)
                return str;
            else {
                //handle encoding key html characters
                str = str.replace(/&/g, '&amp;').replace(/\"/g, '&quot;').replace(/>/g, '&gt;').replace(/</g, '&lt;').replace(/\\/g, '&#92').replace(/\//g, '&#47');
                str = str.replace(/\r\n/g, '<br/>').replace(/\n\r/g, '<br/>').replace(/\r/g, '<br/>').replace(/\n/g, '<br/>').replace(/\t/g, '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;').replace(/\b/g, '');
                return str;
            }
        }

        function removeTabAndSession(sessionId, callNatures, comments) {
            if (sessionId) {
                var tabId = "session" + sessionId;
                var removeSessionId = sessionId;
                Tools.removeItemFromArr(sessionIdsArr, removeSessionId);

                if (sessionIdsArr.length > 0) {
                    if (sessionId == currentSessionId) {
                        //自动切换到另外一个会话
                        var sessionTab = $('#sessionTab_' + sessionId, container);
                        var otherSessionTab = sessionTab.next();
                        if (otherSessionTab.length === 0) {
                            otherSessionTab = sessionTab.prev();
                        }
                        if (otherSessionTab.length > 0) {
                            var otherSessionId = otherSessionTab.attr('id');
                            switchTabAndSession(otherSessionId);
                        }
                        closeSession(sessionId); //disable first so the tab is deleted
                    } else {
                        removeSessionItem(sessionId);
                    }
                } else {
                    callService({
                        data: {
                            requestType: "setCurrentChatSessionId",
                            sessionId: -1
                        }
                    });
                    $("#agentResponseDiv", container).html(formatFeedbackMessage(i18n.feedback.noMoreChatSessions));
                    $('.session_hd .title_name', container).html('');
                    $('.session_hd .session_last', container).html('');
                    closeSession(sessionId);
                    $('.control_panel .content #edit-area', container).html('');
                    currentSessionId = null;
                    //所有的都关闭完了
                    notifyProjectEvent($CONST.ChatEvent.CHAT_SESSION_CHANGED, {
                        sessionId: ''
                    });
                }
                //结束时通知状态栏
                notifyProjectEvent($CONST.ChatEvent.CHAT_SESSION_CLOSED, {
                    sessionId: sessionId
                });

                var sentInfoJson = JSON.stringify({
                    actionType: "closeSession",
                    senderId: c.configs.agentId,
                    senderName: c.staff.displayName,
                    sessionId: removeSessionId,
                    callNatures: typeof (callNatures) === 'string' ? callNatures : JSON.stringify(callNatures),
                    comments: comments
                });
                callService({
                    data: {
                        requestType: "send",
                        data: sentInfoJson
                    }
                }, true);
                refreshSessionStat();
                checkReservedStatusWhenSessionEnd();
            }
        }

        /**
         * 结束指定聊天会话，但不关闭
         */
        function endSession(sessionId, callNatures, comments) {
            if (sessionId) {
                disableSession(sessionId);
                sinkSession(sessionId);
                var session = sessionsMap["session" + sessionId];
                var sentInfoJson = JSON.stringify({
                    actionType: "closeSession",
                    senderId: c.configs.agentId,
                    senderName: c.staff.displayName,
                    sessionId: sessionId,
                    callNatures: typeof (callNatures) === 'string' ? callNatures : JSON.stringify(callNatures),
                    comments: comments
                });
                callService({
                    data: {
                        requestType: "send",
                        data: sentInfoJson
                    }
                }, true);
                if (session) {
                    session.active = false;
                    session.endTime = new Date().getTime();
                }
                refreshSessionStat();
                checkReservedStatusWhenSessionEnd();
            }
        }

        function topSession(sessionId) {
            if ($('#sessionTab_' + sessionId, container).hasClass('top')) {
                $('.session_list .chat_item.top', container).first().before($('#sessionTab_' + sessionId, container));
            } else {
                $('.session_list .chat_item:not(.top)', container).first().before($('#sessionTab_' + sessionId, container));
            }
        }

        function sinkSession(sessionId) {
            if (sinkAfterEnds) {
                var allSessionItems;
                if ($('#sessionTab_' + sessionId, container).hasClass('top')) {
                    allSessionItems = $('.session_list .chat_item.top', container);
                } else {
                    allSessionItems = $('.session_list .chat_item', container);
                }
                //移动session到关闭了的会话的顶部
                $.each(allSessionItems, function (i, sessionItem) {
                    var sId = $(sessionItem).attr('data');
                    if (sId != sessionId) {
                        var s = sessionsMap["session" + sId];
                        if (s && !s.active) {
                            $(sessionItem).before($('#sessionTab_' + sessionId, container));
                            return false;
                        } else {
                            if (i === allSessionItems.length - 1) {
                                $(sessionItem).after($('#sessionTab_' + sessionId, container));
                            }
                        }
                    }
                });
            }
        }

        function createSession(sessionId, client, queueId, queueName, sessionParams, eventGuid) {
            var newSession = new Session(sessionId);
            newSession.clientId = client.id;
            newSession.client = client;
            newSession.lang = client.lang;
            newSession.languageTo = client.lang;
            newSession.queueId = queueId;
            var workgroup = c.findWorkgroupByQueueId(queueId);
            if (workgroup) {
                newSession.workgroupId = workgroup.id;
                newSession.workgroupName = workgroup.name;
            }
            newSession.queueName = queueName;
            newSession.eventGuid = eventGuid || '';
            $F.log("Chat - createSession", sessionId + " clientId " + client.id);
            if (translateAutoEnabled) {
                setTranslateLanguage(sessionId, $.cookie('chat.translateTo') || 'zh', newSession.languageTo)
            }
            if (sessionParams) {
                if (typeof (sessionParams) === 'string') {
                    sessionParams = JSON.parse(sessionParams);
                }
                newSession.serviceAccountId = sessionParams.serviceAccountId;
                newSession.serviceAccountName = sessionParams.serviceAccountName;
                var browserName = '';
                var os = '';
                if (sessionParams.metaData) {
                    browserName = sessionParams.metaData.name;
                    os = sessionParams.metaData.os;
                }
                newSession.sessionParams = {
                    id: sessionId,
                    ip: sessionParams.ip,
                    browser: browserName,
                    os: os,
                    sessionType: sessionParams.sessionType,
                    creationTime: newSession.creationTime,
                    queueId: queueId,
                    queueName: queueName,
                    robotEngine: sessionParams.robotEngine
                };
                $F.log("Chat - createSession", "session params", newSession.sessionParams);
            }
            //set default allow client send attachment
            newSession.allowClientAttach = c.configs.allowClientSendAttachment;
            if (newSession.allowClientAttach) {
                callService({
                    data: {
                        requestType: "sessionSetting",
                        sessionId: sessionId,
                        settingType: "allowClientAttach",
                        value: 1
                    }
                }, true);
            }

            if (clientCache[client.id]) {
                newSession.editingMessage = clientCache[client.id].editMessage || ''
            }
            return newSession;
        }

        /**
         * 根据队列号找到工作组id
         * @param queueId
         * @returns {*}
         */
        this.findWorkgroupByQueueId = function (queueId) {
            var result;
            this.workgroups.some(function (w) {
                var find = w.queues.some(function (q) {
                    return q.id === queueId;
                });
                if (find) {
                    result = w;
                    return true;
                }
            });
            return result;
        };

        /**
         * 暴露出方法给动态页面调用
         * 获取非离线坐席列表
         * @param queueId 队列号
         * @param sessionId 会话id
         * @param workgroupId 工作組id
         */
        this.getNonOfflineAgentList = function (queueId, sessionId, workgroupId) {
            return callService({
                data: {
                    requestType: 'getNonOfflineAgentList',
                    queueId: queueId,
                    sessionId: sessionId,
                    workgroupId: workgroupId
                }
            })
        };
        /**
         * 修改坐席状态
         * @param status 状态id
         * @param retry 是否失败重试
         * @param mute 是否异常时候弹出报错
         */
        this.changeStatus = function (status, retry, mute) {
            var c = this;
            var busyId = $F.busyShow();
            return new Promise(resolve => {
                callService({
                    data: {
                        requestType: "changeStatus",
                        status: status
                    }
                }, retry).done(data => {
                    $F.busyHide(busyId);
                    if (data && data.status === "success") {
                        c.currentUserStatus = status;
                        c.setUserStatus();
                        c.hideUserStatusMenu();
                        resolve(1);
                    } else {
                        if (!mute) {
                            let failMessage = data.message ? data.message : i18n.error.switchStatusFailed;
                            $F.alert(failMessage);
                        }
                        resolve(-1);
                    }
                }).fail(jqXHR => {
                    $F.busyHide(busyId);
                    if (!mute) {
                        $F.alert(i18n.error.switchStatusFailed + ": " + jqXHR.status + ' ' + jqXHR.statusText);
                    }
                    resolve(-1);
                });
            })
        };

        this.changeToExceptionStatus = function () {
            callService({
                data: {
                    requestType: "changeToExceptionStatus"
                }
            });
        };

        /**
         * 添加自定义的工具条按钮，支持传入对象或者数组
         * btn支持属性：
         *    label 标签
         *    tooltip 鼠标放上去后提示
         *    click 点击后回调
         *    style 样式
         *    icon 添加icon
         * @param btns
         */
        this.addCustomToolbarBtns = function (btns) {
            if (btns) {
                if (!$.isArray(btns)) {
                    btns = [btns];
                }
                btns.forEach(btn => {
                    $('.toolbar .chat_custom_btn[title="' + (btn.tooltip || btn.label) + '"]').remove()
                    $('<a style="' + (btn.style || '') + '" class="chat_custom_btn" href="javascript:;" title="' + (btn.tooltip || btn.label) + '">' + (btn.icon ? '<i class="far fa-' + btn.icon + '"></i>' : '') + btn.label + '</a>').on('click', () => {
                        if ($.isFunction(btn.click)) {
                            btn.click({
                                sessionId: currentSessionId,
                                customerGuid: currentCustomerGuid
                            });
                        }
                    }).appendTo($('.toolbar', container));
                })
            }
        };

        /**
         * 更新qq和emoji表情的显隐，提供给动态页面调用，动态页面可以根据当前会话类型自行决定显示qq还是emoji
         * @param options {object} {qq:true, emoji: false}
         */
        this.setEmojiState = function (options) {
            if (options) {
                if (options.qq === true) {
                    $('.chat .exp_hd .qq', container).removeAttr("style");
                    $('.chat .exp_bd .qq', container).removeAttr("style");
                    $('.chat .exp_hd .qq', container).addClass('active');
                    $('.chat .exp_bd .qq', container).addClass('active');
                } else if (options.qq === false) {
                    $('.chat .exp_hd .qq', container).hide();
                    $('.chat .exp_bd .qq', container).hide();
                }
                if (options.emoji === true) {
                    $('.chat .exp_hd .emoji', container).removeAttr("style");
                    $('.chat .exp_bd .emoji', container).removeAttr("style");
                    $('.chat .exp_hd .emoji', container).addClass('active');
                    $('.chat .exp_bd .emoji', container).addClass('active');
                } else if (options.emoji === false) {
                    $('.chat .exp_hd .emoji', container).hide();
                    $('.chat .exp_bd .emoji', container).hide();
                }
            }
        };

        /**
         * 设置某个会话中的允许发送的附件类型
         * @param sessionId 会话id
         * @param acceptExtensions 允许发送的附件文件后缀数组 ['jpg','png','gif','jpeg']
         */
        this.setSessionFileAcceptExtensions = function (sessionId, acceptExtensions) {
            var session = sessionsMap["session" + sessionId];
            if (session && acceptExtensions) {
                session.fileAcceptExtensions = acceptExtensions;
            }
        };

        /**
         * 根据sessionId或者session对象
         * @param sessionId
         */
        this.getSession = function (sessionId) {
            var session = sessionsMap["session" + sessionId];
            return session;
        };

        function getAgentListByQueueId(workgroupId, queueId, callback) {
            var contacts = $('#tcDialog .contacts', container);
            if (currentTIR === 'agentTransferToQueue') {
                contacts.html("");
                callback && callback();
                return;
            }
            c.getNonOfflineAgentList(queueId, currentSessionId, workgroupId).done(function (data) {
                if (data.status === "success") {
                    c.agentList = data.agentList;
                    var filterName = $('#tcDialog .agentname-input', container).val();
                    if (filterName) {
                        var filteredAgentList = c.agentList.filter(function (agent) {
                            var agentName = agent.name || agent.firstName;
                            return agentName.indexOf(filterName) > -1;
                        });
                        setupAgentsForTC(contacts, filteredAgentList, filterName);
                    } else {
                        setupAgentsForTC(contacts, c.agentList);
                    }
                }
            }).fail(function () {
                $F.err('Chat - getAgentListByQueueId', i18n.error.getAgentListFailed);
            }).always(function () {
                callback && callback();
            });
        }


        function setupAgentsForTC(contactsContainer, agentList, filterName) {
            contactsContainer.html("");
            if (agentList.length === 0) {
                contactsContainer.append('<span class="contact_notice">' + i18n.noAgents + '</span>');
            } else {
                contactsContainer.append('<div class="online"><h4 class="contact_title">' + i18n.online + '</h4></div>');
                contactsContainer.append('<div class="notOffline"><h4 class="contact_title">' + i18n.nonOffline + '</h4></div>');
                $.each(agentList, function (i, agent) {
                    let agentName = agent.name || agent.firstName; //为了兼容老版本chat，老的里没返回agent.name
                    if (agent.lastName) {
                        agentName = agentName + " - " + agent.lastName;
                    }
                    if (filterName) {
                        agentName = agentName.replace(filterName, '<span style="background: #f1c40f">' + filterName + '</span>');
                    }
                    agentName = agentName + ' (' + agent.activeSessionCount + ')';
                    $(agent.statusId == 1 ? '.online' : '.notOffline', contactsContainer).append(
                        '<div class="contact_item" data-id="' + agent.id + '">' +
                        '<div class="opt">' +
                        '<i class="web_wechat_choose_wireframe"></i>' +
                        '</div>' +
                        '<div class="info">' +
                        '<h4 class="nickname">' + agentName + '</h4>' +
                        '</div>' +
                        '</div>');
                });
            }

            $('.contact_item', contactsContainer).click(function () {
                $('.contact_item', contactsContainer).removeClass('active');
                $('.contact_item i', contactsContainer).removeClass('web_wechat_choose_green');
                $(this).addClass('active');
                $('i', $(this)).toggleClass('web_wechat_choose_green');
            });
        }


        //刷新预览信息
        function refreshPreMessage(msg, sessionId) {
            if (!msg) {
                $(".pre_message_container", container).html("").hide();
            } else {
                if (c.configs.realtimeCheck) {
                    $(".pre_message_container", container).text($('<div>').html(msg).text()).show();
                }
            }
            if (sessionId)
                sessionsMap["session" + sessionId].previewMessage = msg || '';
        }

        /**
         * 发送系统自动消息
         * @param session 发送的会话
         * @param messageText 消息内容
         * @param messageType 消息类型（自动发送消息的类型）
         * @param successHandler 成功发送回调
         */
        function sendSystemAutoMessage(session, messageText, messageType, successHandler) {
            if (messageText) {
                var sessionId = session.id;
                callService({
                    data: {
                        requestType: 'send',
                        data: getPostMessageSentInfoJSONWithSessionId(messageText, sessionId, messageType)
                    },
                    success: data => {
                        if (data.status === 'success') {
                            const sendResponse = data.sendResponse
                            let traslateMessage = sendResponse ? sendResponse.translateMessage : '';
                            var message = addMessageToSession(sessionId, {
                                id: c.configs.agentId,
                                name: c.staff.displayName
                            }, {
                                text: messageText,
                                read: i18n.message.unRead,
                                newMessageId: sendResponse.newMessageId,
                                translate: traslateMessage,
                                type: messageType,
                                systemNotice: true
                            }, Tools.getFormattedNowTime(true));
                            showMessageOnItem(sessionId, message.text);
                            if (currentSessionId == sessionId) {
                                var displayMessage = getDisplayMessage(message, session);
                                appendTextToDiv(displayMessage);
                                // showMessageOnItem(sessionId, message.text);
                            }
                        }
                        if ($.isFunction(successHandler)) {
                            successHandler(data);
                        }
                    }
                }, true);
            }
        }

        function userWaitingElapseTicker(c) {
            try {
                var now = new Date();
                $.each(sessionsMap, function (key, session) {
                    if (session && session.active && session.permFlag != 1) {
                        if (session.userInWaiting) {
                            var lastAutoReplyElapsed = Math.floor((now.getTime() - session.lastAutoReplyMessageTime) / 1000);
                            if (c.configs.timeoutForClientWait > 0 && lastAutoReplyElapsed >= c.configs.timeoutForClientWait &&
                                (sysAutoReplyMessageTimes == 0 || (sysAutoReplyMessageTimes > 0 && sysAutoReplyMessageTimes > session.autoReplyMessageTimes))) {
                                session.autoReplyMessageTimes++;
                                session.lastAutoReplyMessageTime = now.getTime();
                                $F.log('Chat - userWaitingElapseTicker', 'send autoReplyMessageForClientWaitTimeout for lastAutoReplyElapsed ' + lastAutoReplyElapsed);
                                getPropertyByQueue('autoReplyMessageForClientWaitTimeout', session.queueId, session.lang).then(autoReplayMessage => {
                                    let message = autoReplayMessage,
                                        arms = autoReplayMessage.split('###'); // 可以用###来分隔多条消息，每次发送随机一条消息
                                    if (arms.length > 0) {
                                        message = arms[Math.floor(Math.random() * arms.length)];
                                    }
                                    sendSystemAutoMessage(session, message, MESSAGE_TYPE.USER_IN_WAITING_MESSAGE);
                                });
                            }
                        } else {
                            // 锁定的会话不会触发afk自动关闭逻辑
                            if (!session.locked) { //没有锁定并且没有视频中时执行
                                //handle auto close session by long time no response from client side
                                var afkElapsed = session.lastAgentMessageSentTime;
                                if (afkElapsed == -1) {
                                    afkElapsed = session.creationTime;
                                }
                                afkElapsed = now.getTime() - afkElapsed;
                                var nmd = c.configs.userAFKElapsedNotifyMessageDuration * 1000;
                                if (nmd > 0) {
                                    if (afkElapsed > nmd && !session.hasUserNoResponseNotified) {
                                        //send notification
                                        $F.log('Chat - userWaitingElapseTicker', "session " + session.id + " send user AFK notification.");
                                        session.hasUserNoResponseNotified = true;
                                        session.lastAgentMessageSentTime = now.getTime();
                                        afkElapsed = 0;
                                        getPropertyByQueue('userAFKElapsedNotifyMessage', session.queueId, session.lang).then(userAFKElapsedNotifyMessage => {
                                            sendSystemAutoMessage(session, userAFKElapsedNotifyMessage, MESSAGE_TYPE.USER_AFK_NOTIFY_MESSAGE);
                                        });
                                    }
                                }
                                var csd = c.configs.userAFKElapsedCloseSessionDuration * 1000;
                                if (csd > 0) {
                                    if (afkElapsed > csd && session.hasUserNoResponseNotified && !session.hasCloseAFK) {
                                        //close session
                                        $F.log('Chat - userWaitingElapseTicker', "session " + session.id + " send user AFK close session message.");
                                        getPropertyByQueue('userAFKElapsedCloseSessionMessage', session.queueId, session.lang, '客户AFK，自动结束').then(messageText => {
                                            var sessionId = session.id;
                                            var message = addMessageToSession(sessionId, {
                                                id: c.configs.agentId,
                                                name: c.staff.displayName
                                            }, {
                                                text: messageText,
                                                read: i18n.message.unRead,
                                                type: MESSAGE_TYPE.USER_AFK_CLOSE_SESSION_MESSAGE
                                            });
                                            showMessageOnItem(sessionId, messageText);
                                            if (currentSessionId == sessionId) {
                                                var displayMessage = getDisplayMessage(message, session);
                                                appendTextToDiv(displayMessage);
                                            }
                                            $F.log('Chat - userWaitingElapseTicker', "Close session " + session.id + " due to user AFK");
                                            callService({
                                                data: {
                                                    requestType: 'agentCloseAFKClient',
                                                    sessionId: sessionId,
                                                    agentId: c.configs.agentId,
                                                    data: getPostMessageSentInfoJSONWithSessionId(message.text, sessionId, MESSAGE_TYPE.USER_AFK_CLOSE_SESSION_MESSAGE)
                                                }
                                            }, true).always(function () {
                                                disableSession(sessionId);
                                            });
                                            session.hasCloseAFK = true;
                                            session.lastAgentMessageSentTime = now.getTime();
                                        });
                                    }
                                }
                            }
                        }

                        var timediff = new Date().getTime() - session.lastUserMessageReceivedTime;
                        if (session.userInWaiting) {
                            // 超时变色
                            if (timediff && chatListTimeSytles && chatListTimeSytles.length) {
                                changeItemColorByTime(timediff / 1000, session.id)
                            }
                            //1. 左侧item时间，显示几分钟前，大于1小时，就显示1小时前
                            var chatItem = $('#sessionTab_' + session.id, container);
                            if (chatItem.length > 0) {
                                // var timediff = new Date().getTime() - session.lastUserMessageReceivedTime;
                                if (timediff > 60 * 60 * 1000) {
                                    $('.ext .attr', chatItem).html(i18n.oneHourAgo);
                                } else if (timediff > 60 * 1000) {
                                    $('.ext .attr', chatItem).html((Math.floor(timediff / 60 / 1000)) + i18n.minutesAgo);
                                }
                                //2. 这个时间超时多长时间变红
                                if (!isNaN(lastChattingTimeThreshold) && lastChattingTimeThreshold > 0) {
                                    if (timediff > lastChattingTimeThreshold * 1000) {
                                        $('.ext .attr', chatItem).html('<span style="color: red">' + $('.ext .attr', chatItem).html() + '</span>');
                                    }
                                }
                            }
                        } else {
                            changeItemColorByTime(timediff / 1000, session.id, true)
                        }
                    }
                });

                //3. 聊天总时长显示在聊天右侧顶部
                var sessionCur = sessionsMap["session" + currentSessionId];
                if (sessionCur) {
                    var lastingTime;
                    if (sessionCur.active) {
                        lastingTime = new Date().getTime() - sessionCur.creationTime;
                        $('.session_hd .session_last', container).html(i18n.lasting + ':' + Tools.getFormattedLasting(lastingTime));
                    }
                }

            } catch (e) {
                $F.err('Chat', "Error occurred on userWaitingElapseTicker : " + e.message);
            }
            userWaitingTimer = setTimeout(function () {
                userWaitingElapseTicker(c);
            }, 1000);
        }

        function uploadDataURL(content, fileName, uploadUrl) {
            var busyId = $F.busyShow(i18n.dyn.loading + ': ' + i18n.imageUploading);
            var postData = {
                filename: fileName,
                dataURI: content
            };
            $F.log('Chat - uploadDataURL', 'Upload file: ' + fileName + ', size:' + content.length + ', upload:' + uploadUrl);
            // 上传的数据除了图片外，还可以包含自己需要传递的参数
            var csId = currentSessionId;
            $.ajax({
                type: 'POST',
                url: uploadUrl,
                dataType: "json",
                data: postData,
                cache: false,
                xhrFields: {
                    withCredentials: true
                }
            }).done(function (data) {
                if (data) {
                    if (data.url) {
                        var displayUrl = chatServerHost + data.url,
                            content = '<img data-from="elite" src = "' + data.url + '" >',
                            displayContent = '<img data-from="elite" src = "' + displayUrl + '" style="max-width: 300px;max-height: 300px;vertical-align: baseline" data-url="' + data.url + '">';
                        $F.log('Chat - uploadDataURL', 'UploadDataURL Done, download path:' + displayUrl);
                        if (editImageAfterPaste) {
                            Tools.appendToEditDiv(displayContent);
                        } else {
                            Tools.confirm(i18n.sendCaptureConfirm + '<div>' + displayContent + '</div>', function () {
                                sendMessage(content, csId, displayContent);
                            });
                        }
                    } else {
                        $F.log('Chat - uploadDataURL', 'UploadDataURL Done, download failed:' + data.msg);
                        $F.alert(i18n.error.uploadFailed + ': ' + (data.msg || i18n.error.serverError));
                    }
                }
            }).fail(function (jqXHR) {
                $F.err('Chat - uploadDataURL', 'UploadDataURL failed: ' + jqXHR.status + ' ' + jqXHR.statusText);
                $F.alert(i18n.error.uploadFailed + ': ' + i18n.error.serverError);
            }).always(function () {
                $F.busyHide(busyId);
            });
        }


        function updateClientForSession(session, clientId, clientName) {
            session.clientId = clientId;
            session.client.id = clientId;
            session.client.clientType = 0; //只可能从访客变成客户，这里直接写死type变成客户的type
            setupClientDisplayName(session.client, clientName);
            $('#sessionTab_' + session.id + ' .nickname_text', container).attr('title', session.client.name);
            $('#sessionTab_' + session.id + ' .nickname_text', container).html(session.client.name);
            if (session.id == currentSessionId) {
                $('.session_hd .title_name', container).html(session.client.name + ((session.userCount > 2) ? '(' + session.userCount + ')' : ''));
            }
        }

        /**
         * 显示聊天请求接受对话框
         */
        function showRequestDialog(requestId, user, chatRequestToUser) {
            if (chatPanelIsHidden()) {
                $E.openChatPanel();
                $('.chat', container).show();
            }
            var requestType = chatRequestToUser.requestType;
            var tabTitle = i18n.request;
            if (requestType == CHAT_REQUEST_TYPE['clientRequest'])
                tabTitle = i18n.webRequest;
            else if (requestType == CHAT_REQUEST_TYPE['wechatRequest'])
                tabTitle = i18n.wechatRequest;
            else if (requestType == CHAT_REQUEST_TYPE['agentInvite'])
                tabTitle = i18n.invite;
            else if (requestType == CHAT_REQUEST_TYPE['agentTransfer'] || requestType == CHAT_REQUEST_TYPE['agentTransferToQueue'])
                tabTitle = i18n.transfer;

            $('.requestDialog', container).show().css({
                "paddingTop": ($(container).height() - $('.requestDialog .chat-dialog-content', container).height() - 100) / 2
            });

            var nav = requestTabs.find(".ui-tabs-nav"),
                m = 'request_' + requestId,
                label = tabTitle + '[' + requestId + ']',
                tab = $("<li data-id='" + requestId + "'><a href='#" + m + "'>" + label + "</a></li>"),
                tabContainer = $('<div id="' + m + '" class="request-tab-container" style="padding: 10px;"></div>');
            tabContainer.append(
                '<div class="dialog_bd">' +
                '<div><table>' +
                '<tr><td>' + i18n.name + '</td><td>' + setupClientDisplayName(user) + '</td></tr>' +
                (chatRequestToUser.client ? '<tr><td>' + i18n.clientName + '</td><td>' + setupClientDisplayName(chatRequestToUser.client) + '</td></tr>' : '') +
                (chatRequestToUser.queueName ? '<tr><td>' + i18n.queueName + '</td><td>' + chatRequestToUser.queueName + '</td></tr>' : '') +
                (chatRequestToUser.comments ? '<tr><td>' + i18n.comments + '</td><td>' + chatRequestToUser.comments + '</td></tr>' : '') +
                '</table></div>' +
                '<div class="progress"></div>' +
                '</div>' +
                '<div class="dialog_ft">' +
                '<a href="javascript:;" class="btn btn_primary accept">' + i18n.accept + '</a>' +
                '<a href="javascript:;" class="btn btn_primary reject">' + i18n.reject + '</a>' +
                '</div>');
            $('.accept', tabContainer).one('click', function () {
                $F.log('Chat', 'Request ' + requestId + ' accepted');
                sendRequestResponse(requestId, 'yes');
                closeRequestTab(tab, requestId);
            });
            $('.reject', tabContainer).one('click', function () {
                $F.log('Chat', 'Request ' + requestId + ' rejected');
                sendRequestResponse(requestId, 'no');
                closeRequestTab(tab, requestId);
            });
            var requestProgress = $('.progress', tabContainer).progressbar({
                max: c.configs.timeoutForAccept,
                value: c.configs.timeoutForAccept,
                create: function (event) {
                    $('.progress .ui-progressbar-value', tabContainer).css({
                        margin: 0,
                        border: 0
                    });
                }
            });

            var updateRequestProgress = function (requestProgress) {
                var value = requestProgress.progressbar("value");
                if (value > 0) {
                    requestProgress.progressbar("value", value - 1);
                    if (value > c.configs.timeoutForAccept / 3 * 2) {
                        $('.progress .ui-progressbar-value', tabContainer).css({
                            background: '#3caf36'
                        });
                    } else if (value > c.configs.timeoutForAccept / 3) {
                        $('.progress .ui-progressbar-value', tabContainer).css({
                            background: '#FFCE42'
                        });
                    } else {
                        $('.progress .ui-progressbar-value', tabContainer).css({
                            background: '#DD5044'
                        });
                    }

                    requestProgressTimer[requestId] = setTimeout(function () {
                        updateRequestProgress(requestProgress);
                    }, 1000);
                } else {
                    sendRequestResponse(requestId, 'timeout');
                    closeRequestTab(tab, requestId);
                }
            }

            tab.data('container', tabContainer);
            tab.appendTo(nav);
            requestTabs.append(tabContainer);
            setTimeout(function () {
                requestTabs.tabs("refresh");
                var tabIdx = $('li', nav).length;
                requestTabs.tabs({
                    active: tabIdx - 1
                });
                $('.request-tab-container', requestTabs).css('height', 'auto');

                updateRequestProgress(requestProgress);
            }, 100);

            function closeRequestTab(tab, requestId) {
                if (requestProgressTimer[requestId]) {
                    clearTimeout(requestProgressTimer[requestId]);
                }

                tab.data("container").remove();
                tab.remove();
                requestTabs.tabs("refresh");
                if ($('li', nav).length === 0) {
                    $('.requestDialog', container).hide();
                }
            }

            notifyMe();
        }

        /**
         * 显示聊天请求接受对话框
         */
        function showCloseSessionDialog(sessionId) {
            $('.closeSessionDialog', container).show().css({
                "paddingTop": ($(container).height() - $('.closeSessionDialog .chat-dialog-content', container).height() - 100) / 2
            });
            var confirmContainer,
                sessionCur = sessionsMap["session" + sessionId],
                closeMessage = c.configs.onAgentSessionCloseMessage ? $F.ognl(c.configs.onAgentSessionCloseMessage, {
                    name: sessionCur.client.name
                }) : i18n.closeSessionConfirm + '[' + sessionCur.client.name + ']';
            if (sessionCur) {
                if (sessionCur.active) {
                    confirmContainer = $(
                        '<div class="chat-dialog-content">' +
                        '<span class="fa-stack fa-lg confirm"><i class="fa fa-circle fa-stack-2x"></i><i class="fa fa-question fa-stack-1x fa-inverse"></i></span>' +
                        '<span style="padding-left: 10px;">' + closeMessage + '</span>' +
                        '</div>' +
                        '<div class="dialog_ft" style="text-align: right;">' +
                        '<a href="javascript:;" class="btn btn_primary end" style="width: 110px;text-align: center;padding: 3px 20px">' + i18n.end + '</a>' +
                        '<a href="javascript:;" class="btn btn_primary close" style="width: 110px;text-align: center;padding: 3px 20px">' + i18n.endAndClose + '</a>' +
                        '</div></div>');
                    $('.end', confirmContainer).on('click', function () {
                        if (isVideoCallIng) {
                            if (sessionId == currentVideoSessionId) {
                                sendMessage(`${i18n.videoMeetingRecord.Connect} ${formatTimestampDiff(new Date().getTime() - meetingStartTime)} [videoMeet]`, currentVideoSessionId, undefined, undefined, function () {
                                    endSession(sessionId);
                                    $('.closeSessionDialog', container).hide();
                                    if (videoSdkType === 'sanTi') {
                                        $('.video-meeting-btn').css('display', 'none')
                                        $('#selfvideo', container).hide();
                                        $('#othervideo', container).hide();
                                        $('.meeting', container).hide();
                                        isVideoCallIng = false
                                        if (globalMeeting) {
                                            globalMeeting.leaveRoom()
                                            globalMeeting.levelRoomRequest()
                                        }
                                    } else if (videoSdkType === 'juFeng') {
                                        $('.video-meeting-btn').css('display', 'none')
                                        $('#selfvideo', container).hide();
                                        $('#othervideo', container).hide();
                                        $('.jufengMeeting', container).hide();
                                        isVideoCallIng = false
                                        if (globalMeeting) {
                                            globalMeeting.leaveRoom()
                                            globalMeeting.levelRoomRequest()
                                        }
                                    }
                                })
                            }
                        } else {
                            endSession(sessionId);
                            $('.closeSessionDialog', container).hide();
                        }


                    });
                    $('.close', confirmContainer).on('click', function () {
                        if (isVideoCallIng) {
                            if (sessionId == currentVideoSessionId) {
                                sendMessage(`${i18n.videoMeetingRecord.Connect} ${formatTimestampDiff(new Date().getTime() - meetingStartTime)} [videoMeet]`, currentVideoSessionId, undefined, undefined, function () {
                                    removeTabAndSession(sessionId);
                                    $('.closeSessionDialog', container).hide();
                                    if (videoSdkType === 'sanTi') {
                                        $('.video-meeting-btn').css('display', 'none')
                                        $('#selfvideo', container).hide();
                                        $('#othervideo', container).hide();
                                        $('.meeting', container).hide();
                                        isVideoCallIng = false
                                        if (globalMeeting) {
                                            globalMeeting.leaveRoom()
                                            globalMeeting.levelRoomRequest()
                                        }
                                    } else if (videoSdkType === 'juFeng') {
                                        $('.video-meeting-btn').css('display', 'none')
                                        $('#selfvideo', container).hide();
                                        $('#othervideo', container).hide();
                                        $('.jufengMeeting', container).hide();
                                        isVideoCallIng = false
                                        if (globalMeeting) {
                                            globalMeeting.leaveRoom()
                                            globalMeeting.levelRoomRequest()
                                        }
                                    }
                                })
                            }
                        } else {
                            removeTabAndSession(sessionId);
                            $('.closeSessionDialog', container).hide();
                        }

                    });
                } else {
                    confirmContainer = $(
                        '<div class="chat-dialog-content">' +
                        '<span class="fa-stack fa-lg confirm"><i class="fa fa-circle fa-stack-2x"></i><i class="fa fa-question fa-stack-1x fa-inverse"></i></span>' +
                        '<span style="padding-left: 10px;">' + closeMessage + '</span>' +
                        '</div>' +
                        '<div class="dialog_ft" style="text-align: right;">' +
                        '<a href="javascript:;" class="btn btn_primary close" style="width: 110px;text-align: center;padding: 3px 20px">' + i18n.confirm + '</a>' +
                        '</div></div>');
                    $('.close', confirmContainer).on('click', function () {
                        removeTabAndSession(sessionId);
                        $('.closeSessionDialog', container).hide();
                    });
                }
                $('.closeSessionDialog .confirmContainer', container).empty().append(confirmContainer);
            }
        }

        /**
         * 显示消息预览对话框
         * 如果是formattedMsg，则弹出这个消息的预览
         * @param msg
         */
        function showMessagePreviewDialog(msg) {
            $('.messagePreviewDialog', container).show().css({
                "paddingTop": ($(container).height() - $('.messagePreviewDialog .chat-dialog-content', container).height() - 100) / 2
            });

            $('.messagePreviewDialog .dialog_bd', container).empty().append(Tools.parseFormattedMsg(msg));
        }

        /**
         * 截图上传
         * @param mode 这个是截图的类型 0是隐藏当前窗口，1是不隐藏当前窗口
         */
        function capture(mode) {
            mode = mode || 0;
            var fileExtension = 'png',
                now = new Date(),
                tmpFileName = now.getTime() + "." + fileExtension,
                uploadURL = chatServerAddr + '/uploadDataURI';
            $F.log('Chat - screencut', 'Start screen cut: ' + tmpFileName);
            H5Utils.captureWithoutWS(tmpFileName, mode, function (d) {
                if (d.code == -1) {
                    $F.info(d.message);
                } else {
                    uploadDataURL(d.content, d.tmpFileName, uploadURL);
                }
            }, uploadURL, false);
        }

        function setTypingNotice(notice) {
            $('.session_hd .typing', container).html(i18n.typing);
            if (typingTimer) { //reset last timer to start over
                clearTimeout(typingTimer);
            }
            typingTimer = setTimeout(removeTypingNotice, 10 * 1000);
            if (!typingEffectTimer) {
                typingEffectTimer = setInterval(function () {
                    $('.session_hd .typing', container).css('color', '#' + Math.floor(Math.random() * 16777215).toString(16));
                }, 500);
            }
        }

        function removeTypingNotice() {
            if (typingEffectTimer) {
                clearInterval(typingEffectTimer);
                typingEffectTimer = null;
            }
            $('.session_hd .typing', container).html('');
        }

        function postMessageStart(event, params) {
            if (showAgentAssistIframe && showAgentAssistIframe.id && showAgentAssistIframe.url) {
                const agentAssistIframe = $("#" + showAgentAssistIframe.id).prop('contentWindow');
                if (agentAssistIframe) {
                    if ($CONST.ChatEvent.CHAT_SESSION_CREATED === event) { //创建会话
                        agentAssistIframe.postMessage({
                            action: "sessionStart",
                            sessionId: params.sessionId, //开始会话ID
                            lang: params.session.client.lang, //中英文
                            from: params.session.client.targetId, //来源
                            client: {
                                id: params.session.clientId, //客户ID
                                type: params.session.client.clientType, //客户类型 0 登录客户 1 访客
                                name: params.session.client.firstName !== '' ? params.session.client.firstName : params.session.client.alias //客户名称
                            }
                        }, '*');
                    } else if ($CONST.ChatEvent.CHAT_SESSION_CHANGED === event && params.sessionId !== '') {
                        //切换会话
                        agentAssistIframe.postMessage({
                            action: "sessionChange",
                            sessionId: params.sessionId // 会话ID
                        }, '*');
                    } else if ($CONST.ChatEvent.CHAT_SESSION_CLOSED === event) { //结束会话
                        agentAssistIframe.postMessage({
                            action: "sessionEnd",
                            sessionId: params.sessionId // 会话ID
                        }, '*');
                    } else if ($CONST.ChatEvent.NEW_MESSAGE_RECEIVED === event) {
                        agentAssistIframe.postMessage({
                            action: "postMessage",
                            sessionId: params.sessionId, // 会话ID
                            text: params.message.text //需要搜索的消息内容
                        }, '*');
                    }
                }
            }
        }

        /**
         * 监听来自页面的内容
         */
        window.addEventListener('message', function (e) {
            if (e.data.action === 'addMessageToTextarea' && e.data.content.trim() !== '')
                var curSession = sessionsMap["session" + currentSessionId];
            if (curSession && curSession.active) {
                $('.control_panel .content #edit-area', container).text(e.data.content.trim());
            }
        }, false);

        function notifyProjectEvent(event, params) {
            try {
                postMessageStart(event, params);
                project.events.notify(event, params);
            } catch (e) {
                $F.err('Chat - notifyProjectEvent', e);
            }
        }

        function chatUnload() {
            $F.log('Chat - unload', 'On window unload');
            if (c.configs.agentExceptionStatus) {
                c.changeToExceptionStatus();
            } else {
                c.logout();
            }
        }

        function setTranslateLanguage(currentSessionId, agentLang, targetLang) {
            callService({
                data: {
                    requestType: 'selectTranslateLanguage',
                    sessionId: currentSessionId,
                    languageAgent: agentLang,
                    languageClient: targetLang
                }
            }).done(function (data) {
                if (data.status === 'success') {
                    var sessionCur = sessionsMap["session" + currentSessionId];
                    sessionCur.languageTo = targetLang;
                }
            }).fail(function (e) {
                $F.err('Chat - translate', i18n.error.translateFailed);
            });
        }

        //设置工具栏的下来列表语言
        function setLanguageSelect(lang) {
            if (translateAutoEnabled) {
                $('#trans_select').val(lang).trigger("change");
            }
        }

        /**
         * 在现在这种webworker模式下，是否还需要checkPull?
         */
        function startCheckPull() {
            checkPullTimer = setInterval(function () {
                if (lastPullTime && new Date().getTime() - lastPullTime > 15000) {
                    $F.log('Chat - checkPull', 'Pull error, restart pull');
                    c.worker.postMessage({
                        cmd: 'doPull',
                        params: {
                            timeout: c.configs.timeoutForPull
                        }
                    });
                } else {
                    $F.log('Chat - checkPull', 'Check pull successful.')
                }
            }, 15000);
        }

        /**
         * 发送转接会议请求
         */
        function sendTCRequest(sendInfo) {
            return new Promise(resolve => {
                callService({
                    data: {
                        requestType: 'send',
                        data: JSON.stringify(sendInfo)
                    }
                }).done(function (jsonResponse) {
                    if (jsonResponse.status === 'success') {
                        var sendResponse = jsonResponse.sendResponse;
                        var chatRequestId = sendResponse.chatRequestId;
                        requestIdsArr[requestIdsArr.length] = chatRequestId;
                        var feedbackMessage = sendResponse.feedbackMessage;
                        appendTextToDiv(formatFeedbackMessage(feedbackMessage));
                        var sessionId = sendResponse.sessionId;
                        if (sessionId) {
                            addMessageToSession(sessionId, {
                                id: "-1",
                                name: null
                            }, {
                                text: formatFeedbackMessage(feedbackMessage),
                                type: MESSAGE_TYPE.SYSTEM
                            });
                        }
                        $('#tcDialog', container).hide();
                        resolve(1);
                    } else {
                        $F.alert(i18n.error.requestFailed);
                        resolve(0);
                    }
                }).fail(function () {
                    $F.alert(i18n.error.requestFailed);
                    resolve(0);
                });
            })
        }

        /**
         * 发送正在输入的通知消息
         */
        function sendTypingNotice() {
            var now = new Date().getTime();
            if ((now - lastTypingNotificationTime) >= (typingNotificationDelay * 1000)) {
                lastTypingNotificationTime = now;
                callService({
                    data: {
                        requestType: 'sendTypingNotification',
                        sessionId: currentSessionId
                    }
                });
            }
        }

        function getIntParam(project, name) {
            return $F.parseInt(project.getParam(name));
        }

        function sendMessages() {

        }

        this.userStatusElapseTicker = function (us) {
            const c = this;
            let lastingTime = new Date().getTime() - c.currentUserStatusStartTime;
            if (us.timeout && lastingTime > us.timeout * 1000) {
                $(".header .display_name .name_extra", c.container).addClass('timeout');
            }
            userStatusTimer = setTimeout(function () {
                $(".header .display_name .user_status", c.container).text(us.userStatus + ': ' + Tools.getFormattedLasting(lastingTime, 1));
                c.userStatusElapseTicker(us);
            }, 1000);
        };

        this.setUserStatus = function () {
            var c = this;
            $(".header .display_name .name_extra", c.container).removeClass('timeout');
            c.currentUserStatusStartTime = new Date().getTime();
            if (userStatusTimer) {
                clearTimeout(userStatusTimer);
            }
            var us = c.userStatus[c.currentUserStatus];
            $('.chat .user_status_menu ul>li>a .selected', c.container).remove();
            $('.chat .user_status_menu ul>li[data-status=' + c.currentUserStatus + ']>a', c.container).append('<i class="selected fa fa-check" />');
            if (c.currentUserStatus !== USER_STATUS.ONLINE) {
                $(".header .display_name .user_status", c.container).text(us.userStatus).removeClass('online');
                c.userStatusElapseTicker(us);
            } else {
                $(".header .display_name .user_status", c.container).text(us.userStatus).addClass('online');
            }
        };

        this.hideUserStatusMenu = function () {
            clearTimeout(this.statusChangeHoverTimer);
            $('.chat .user_status_menu', this.container).css('left', 195).hide();
        };

        this.listWaitingRequests = function (allQueues = false) {
            return callService({
                data: {
                    requestType: "listWaitingRequests",
                    allQueues: allQueues
                }
            });
        };

        /**
         * 预览Formatted消息
         * @param msg
         */
        this.previewFormattedMessage = function (msg) {
            $('.messagePreviewDialog .confirm', container).data('sessionId', currentSessionId).data('msg', msg);
            if (Tools.isFormattedMsg(msg)) {
                showMessagePreviewDialog(msg);
            } else {
                $F.notify(i18n.error.normalMsgNeedNotPerview, 0);
            }
        };

        this.logoutAndClose = function () {
            this.closeChat();
            this.logout().finally(() => {
                notifyProjectEvent($CONST.ChatEvent.CHAT_LOGGED_OUT || 'CHAT_LOGGED_OUT');
            });
        };

        this.logout = function () {
            return new Promise((resolve, reject) => {
                callService({
                    url: 'logout_.do'
                }).done(function () {
                    $F.log("Chat - logout", "logout success");
                    resolve();
                }).fail(function (jqXHR) {
                    $F.log('Chat - logout', 'logout failed', jqXHR);
                    $F.alert(i18n.error.loggedOutFailed + ': ' + jqXHR.status + ' ' + jqXHR.statusText);
                    reject();
                });
            })
        };

        this.closeChat = function () {
            c.logined = false;
            $('.c_chat i').css('color', '#afb5b9')
            for (var requestId in requestProgressTimer) {
                if (requestProgressTimer[requestId]) {
                    clearTimeout(requestProgressTimer[requestId]);
                }
            }
            if (userWaitingTimer) {
                clearTimeout(userWaitingTimer);
            }
            if (updatePermSessionsTimer) {
                clearTimeout(updatePermSessionsTimer);
            }
            if (checkPullTimer) {
                clearInterval(checkPullTimer);
            }
            c.worker.terminate();
            $(window).off('unload', chatUnload);
            c.pluginDom.remove().html('');
            //清除instance缓存
            project.removeModuleInstance($CONST.ModuleName.CHAT);
            project.events.remove('chat');
            $.contextMenu('destroy', '.chat .message.me .bubble_cont.ns');
            $.contextMenu('destroy', '.chat .message:not(.me,.translated,.no-translate) .plain');
            if (!this.parameters.fromDyn) {
                //收起右边页
                $E.openChatPanel();
            }
        };

        var init = function () {
            var busyId = $F.busyShow(i18n.dyn.loading + ': ' + i18n.loggingChat),
                startPaste = false;
            compatibleOldNgsFunctions(c.project); //兼容老版本的ngs
            if (c.project.getParam('CHALOC') === 'embed') {
                $('.configDialog .swapRLContainer', container).hide();
            }
            $(window).on('unload', chatUnload);
            // 初始化title闪烁
            c.originalTitle = document.title;
            $(window).on('focus', () => {
                if (c.blinkTitleTimer) {
                    clearInterval(c.blinkTitleTimer);
                    c.blinkTitleTimer = undefined;
                    document.title = c.originalTitle;
                }
            });
            // 关闭cloak显示
            $('.chat_cloak').hide()
            //刚进页面时候，禁止输入
            $('.btn_send', container).attr('disabled', true);
            $('.control_panel .content #edit-area', container).attr('contenteditable', false);
            $('.toolbar', container).css("display", "none");
            $('.chat_face,.chat_screencut,.chat_pic,.chat_push_rating,.chat_translate_container', container).css("display", "none");
            $('.chat_minimax_summary', container).css('display', 'none');
            $('.chat_minimax_optimization', container).css('display', 'none');
            $('.chat_minimax_ai_response', container).css('display', 'none');

            //点击其他位置时候，隐藏所有popup菜单
            $('.chat', container).click(function () {
                $('#mmpop_system_menu', container).hide();
                c.hideUserStatusMenu();
                $('#mmpop_emoji_panel', container).hide();
                $('.session_more_menu', container).hide();
            });

            //注册发送按钮
            $('.control_panel .btn_send', container).click(tools.debounce(function () {
                if (c.sending) return;
                var content = $('.control_panel .content #edit-area', container).html();
                // $('.control_panel .content #edit-area', container).html("");
                if (content) {
                    if (maxSendingWordLength > 0 && content.length > maxSendingWordLength) { // 超过最大发送消息长度
                        $F.alert(i18n.error.maxWordLengthExceed.format(maxSendingWordLength));
                        return;
                    }
                    try {
                        c.sending = true;
                        var sendContent = content;
                        if (Tools.isFormattedMsg(content)) { // 格式化消息不去做特殊处理
                            sendContent = content = $('.control_panel .content #edit-area', container).text();
                        } else {
                            $("<div>").append(sendContent).find('img.qqemoji').each(function () {
                                var regexp = new RegExp($("<div>").append(this).html().replace("[", "\\[").replace("]", "\\]"), "gi");
                                var emojiText = $(this).attr('text');
                                //emojiText = emojiText.substring(0, emojiText.indexOf("_"));
                                sendContent = sendContent.replace(regexp, '[' + emojiText + ']');
                            });
                            $("<div>").append(sendContent).find('img.standard-emoji').each(function () {
                                var regexp = new RegExp($("<div>").append(this).html(), "gi");
                                var emojiText = $(this).attr('text');
                                sendContent = sendContent.replace(regexp, '[' + emojiText + ']');
                            });
                            //禁用词过滤
                            if (chatSensitiveSend && c.forbidWords) {
                                var forbidWord = false,
                                    alertForbidContent = content;
                                c.forbidWords.forEach(function (dw) {
                                    if (content.indexOf(dw) > -1) {
                                        forbidWord = true;
                                        var dwReg = new RegExp(dw, 'g');
                                        alertForbidContent = alertForbidContent.replace(dwReg, '<span class="dw-highlight">' + dw + '</span>');
                                    }
                                });
                                if (forbidWord) {
                                    return Tools.confirmPrompt(i18n.prohibitSendingProhibitedWords + ': \n<div>' + alertForbidContent + '</div>', function () {
                                        c.sending = false;
                                    });
                                }
                            }

                            //脏词过滤
                            if (dirtyWordsScanEnabled) {
                                if (c.dirtyWords) {
                                    var findDirtyWord = false,
                                        alertContent = content;
                                    c.dirtyWords.forEach(function (dw) {
                                        if (content.indexOf(dw) > -1) {
                                            findDirtyWord = true;
                                            var dwReg = new RegExp(dw, 'g');
                                            alertContent = alertContent.replace(dwReg, '<span class="dw-highlight">' + dw + '</span>');
                                        }
                                    });
                                    if (findDirtyWord) {
                                        return Tools.confirm(i18n.sendingConfirm + ': \n<div>' + alertContent + '</div>', function () {
                                            sendMessage(sendContent, currentSessionId, content, undefined, function () {
                                                $('.control_panel .content #edit-area', container).html("");
                                                c.sending = false;
                                            }, function () {
                                                c.sending = false;
                                            });
                                        }, function () {
                                            c.sending = false;
                                        });
                                    }
                                }
                            }

                            if (editImageAfterPaste) { // 如果开启了编辑粘贴进来的图片的话，需要在发送之前再把图片的url改成真正要发的url
                                $("<div>").append(sendContent).find('img[data-from=elite]').each(function () {
                                    var regexp = new RegExp(Tools.parseForRegexp($("<div>").append(this).html()), "gi");
                                    sendContent = sendContent.replace(regexp, '<img src="' + $(this).attr('data-url') + '" />');
                                })
                            }

                        }

                        sendMessage(sendContent, currentSessionId, content, undefined, function () {
                            $('.control_panel .content #edit-area', container).html("");
                            c.sending = false;
                        }, function (data, $domMessage) {
                            c.sending = false;
                            $('.failed', $domMessage).css("display", 'inline-block');
                            $('.failed', $domMessage).css("display", 'inline-block').one("click", function () {
                                const session = sessionsMap["session" + currentSessionId];
                                if (session.active) {
                                    $($domMessage).remove();
                                    sendMessage(sendContent, currentSessionId, content, undefined, function () {
                                        c.sending = false;
                                    }, function () {
                                        c.sending = false;
                                        $('.failed', $domMessage).css("display", 'inline-block');
                                    });
                                }
                            });
                        });
                    } catch (e) {
                        $F.err('Send Message Error: ' + e.message)
                    } finally {
                        c.sending = false;
                    }
                }
            }, 500, true));

            //注册enter发送还是ctrl+enter发送切换按钮
            $('.control_panel .btn_send_switcher', container).click(function () {
                $('.control_panel .send_switcher_ul', container).toggle();
            });

            $('.control_panel .send_switcher_ul li', container).hover(function () {
                $(this).addClass('active');
            }, function () {
                $(this).removeClass('active');
            });

            $('.control_panel .send_switcher_ul li', container).click(function () {
                sendType = $(this).attr('data-type');
                $.cookie("chat_plugin_send_type", sendType);
                $('.control_panel .action .desc', container).text($(this).text());
                $('.control_panel .send_switcher_ul', container).hide();
            });


            //监听编辑区，按键事件
            $('.control_panel .content #edit-area', container).on({
                'keydown': function (event) {
                    //回车事件
                    if (event.keyCode === 13) {
                        if (!$('.chat-suggestion', container).is(':hidden')) {
                            if ($('.chat-suggestion li.selected', container).length > 0) {
                                $('.chat-suggestion li.selected', container).trigger('click');
                            } else {
                                $('.chat-suggestion', container).hide();
                            }
                        } else {
                            if (sendType == 0) {
                                if (event.ctrlKey || event.shiftKey || event.altKey || event.metaKey) {
                                    var n = "<br>";
                                    if (window.getSelection) {
                                        var r = window.getSelection().focusNode.nextSibling;
                                        do
                                            if (!r || r.nodeValue || "BR" === r.tagName)
                                                break;
                                        while (r = r.nextSibling);
                                        r || (n += n)
                                    }
                                    Tools.appendToEditDiv(n);
                                    $(this)[0].scrollTop = $(this)[0].scrollHeight;
                                } else {
                                    $('.control_panel .btn_send').click();
                                }
                            } else {
                                if (event.ctrlKey || event.shiftKey || event.altKey || event.metaKey) {
                                    $('.control_panel .btn_send').click();
                                } else {
                                    var n = "<br>";
                                    if (window.getSelection) {
                                        var r = window.getSelection().focusNode.nextSibling;
                                        do
                                            if (!r || r.nodeValue || "BR" === r.tagName)
                                                break;
                                        while (r = r.nextSibling);
                                        r || (n += n)
                                    }
                                    Tools.appendToEditDiv(n);
                                    $(this)[0].scrollTop = $(this)[0].scrollHeight;
                                }
                            }
                        }
                        event.preventDefault();
                    }
                    //快捷键 alt+0到9 ctrl+alt+0到9
                    if (event.keyCode >= 48 && event.keyCode <= 57) {
                        if (event.altKey) {
                            var key = 'alt';
                            if (event.ctrlKey && event.altKey) {
                                key = 'ctrl+alt';
                            }
                            key = key + '+' + (event.keyCode - 48);
                            $F.log('Chat - keydown', 'key: ' + key);
                            notifyProjectEvent($CONST.ChatEvent.SHORTCUT, {
                                sessionId: currentSessionId,
                                key: key
                            });
                        }
                    }

                    // ESC
                    if (event.keyCode === 27) {
                        $('.chat-suggestion', container).hide();
                    }
                    // 箭头上，箭头下
                    if (event.keyCode === 38 || event.keyCode === 40) {
                        if (!$('.chat-suggestion', container).is(':hidden')) {
                            event.stopPropagation();
                            event.preventDefault();
                            var lis = $('.chat-suggestion li', container),
                                selectedLi = $('.chat-suggestion li.selected', container);
                            if (event.keyCode === 40) {
                                if (selectedLi.length > 0) {
                                    lis.removeClass('selected');
                                    let index = lis.index(selectedLi) + 1;
                                    index = index % lis.length;
                                    lis.eq(index).addClass('selected');
                                } else {
                                    lis.first().addClass('selected');
                                }
                            }
                            if (event.keyCode === 38) {
                                if (selectedLi.length > 0) {
                                    lis.removeClass('selected');
                                    let index = lis.index(selectedLi) - 1;
                                    index = index % lis.length;
                                    lis.eq(index).addClass('selected');
                                } else {
                                    lis.last().addClass('selected');
                                }
                            }
                        }
                    }

                    //截图快捷键
                    if (event.shiftKey && event.altKey && event.keyCode === 90) {
                        //隐藏当前窗口截图
                        capture();
                        event.stopPropagation();
                        event.preventDefault();
                    } else if (event.shiftKey && event.altKey && event.keyCode === 65) {
                        //不隐藏当前窗口截图
                        capture(1);
                        event.stopPropagation();
                        event.preventDefault();
                    }
                },
                'keyup': function (e) {
                    //backspace
                    if (e.keyCode === 8) { //当删除内容时候，如果是换行后删除换行，需要多删除一个br
                        var html = $('.control_panel .content #edit-area', container).html();
                        if (html.endsWith('<br>')) {
                            $('.control_panel .content #edit-area', container).html('');
                            Tools.appendToEditDiv(html.substring(0, html.length - 4));
                        }
                    }
                },
                'drop': function (event) {
                    // 防止拖拽内容进输入框
                    event.preventDefault();
                    event.stopPropagation();
                },
                click: function (e) {
                    var childNodes = e.target.childNodes;
                    if (childNodes.length > 1) {
                        var lastNodes = childNodes[childNodes.length - 1];
                        if ('IMG' === lastNodes.tagName) {
                            if (e.offsetX > lastNodes.offsetWidth + lastNodes.offsetLeft) {
                                if (e.offsetY > lastNodes.offsetTop - lastNodes.offsetHeight) {
                                    document.execCommand('selectAll', false, null);
                                    document.getSelection().collapseToEnd();
                                }
                            }
                        }
                    }
                },
                //响应paste事件，获取纯文本数据，过滤html的内容（chrome可用）
                'paste': function (e) {
                    const csId = currentSessionId;
                    const clipboardData = e.originalEvent.clipboardData || window.clipboardData;
                    if (clipboardData && clipboardData.items) {
                        // 循环处理items
                        // 如果items里有文件，则上传第一个，并结束
                        // 如果items里没有文件，则获取text
                        // 禁止默认粘贴行为，避免图片内容带有dataURI，造成消息内容过大
                        let findFile = false;
                        $.each(clipboardData.items, (i, item) => {
                            if (item.kind === 'file' && item.type.match('^image/')) {
                                e.stopPropagation();
                                e.preventDefault();
                                const file = item.getAsFile();
                                if (file) {
                                    findFile = true;
                                    uploadOneImage(file, csId);
                                    return false;
                                }
                            }
                        });
                        if (findFile)
                            return;
                    }

                    //如果都没找到剪贴板中有文件
                    const pastedData = clipboardData.getData('Text');
                    if (pastedData) {
                        if (document.body.createTextRange) {
                            // ie11
                        } else {
                            e.stopPropagation();
                            e.preventDefault();
                            document.execCommand('insertText', false, pastedData);
                        }
                    } else {
                        e.stopPropagation();
                        e.preventDefault();
                    }
                }
            });


            const uploadOneImage = (file, sessionId) => {
                if (uploadDisabled) { // 如果禁用上传，则直接return
                    return;
                }
                if (startPaste) return; // 避免重复粘贴
                startPaste = true;
                const tmpFileName = new Date().getTime() + ".png";
                const fd = new FormData();
                fd.append('file', file);
                fd.append('fileName', tmpFileName);
                $.ajax({
                    type: 'POST',
                    url: chatServerAddr + '/uploadh5',
                    data: fd,
                    processData: false,
                    contentType: false,
                    xhrFields: {
                        withCredentials: true
                    }
                }).done(function (data) {
                    if (data.status === 'success') {
                        if (data.url) {
                            var displayUrl = chatServerHost + data.url,
                                content = '<img data-from="elite" src = "' + data.url + '" >',
                                displayContent = '<img data-from="elite" src = "' + displayUrl + '" style="max-width: 300px;max-height: 300px;vertical-align: baseline" data-url="' + data.url + '">';
                            $F.log('Chat - uploadh5', 'Upload done, download path:' + displayUrl);
                            if (editImageAfterPaste) {
                                Tools.appendToEditDiv(displayContent);
                                Tools.addImageClick($('#edit-area'), chatServerAddr)
                            } else {
                                Tools.confirm(i18n.sendImageConfirm + '<div>' + displayContent + '</div>', function () {
                                    sendMessage(content, sessionId, displayContent);
                                });
                            }
                        } else {
                            $F.log('Chat - uploadh5', 'Upload done, download failed:' + data.msg);
                            $F.alert(i18n.error.uploadFailed + ': ' + (data.msg || i18n.error.serverError));
                        }
                    } else {
                        $F.alert(i18n.error.uploadFailed + ': ' + data.message);
                    }
                }).always(function () {
                    startPaste = false;
                });
            };

            // 输入框建议检索
            if (project.esc && suggestionSearchEnabled) {
                $('.control_panel .content #edit-area', container).on('keyup', function (e) {
                    if (!customSuggestionEnabled) {
                        return;
                    }
                    if (e.keyCode === 13 || e.keyCode === 38 || e.keyCode === 40 || e.keyCode === 27) {
                        return;
                    }
                    if (suggestionSearchTimer) {
                        clearTimeout(suggestionSearchTimer);
                        suggestionSearchTimer = null;
                    }
                    suggestionSearchTimer = setTimeout(function () {
                        var content = $('.control_panel .content #edit-area', container).text();
                        //有输入内容，且内容在suggestionMaxWordLength个字以内（内容多于suggestionMaxWordLength个字认为他自己想输入，不想用建议）
                        if (content && content.length < suggestionMaxWordLength) {
                            //当上次搜索内容和这次一样，且结果还显示着的时候，就不再次去查询了
                            if (lastSearchContentForSuggestion === content) {
                                if (!$('.chat-suggestion', container).is(':hidden')) {
                                    return;
                                }
                            }
                            lastSearchContentForSuggestion = content;
                            project.esc.search({
                                "query": {
                                    "bool": {
                                        "filter": [{
                                            "terms": {
                                                "user_id": [$E.staff.id, '']
                                            }
                                        }],
                                        "must": [{
                                            "match": {
                                                "content": content
                                            }
                                        }]
                                    }
                                }
                            }, function (resp) {
                                if (resp.hits.hits && resp.hits.hits.length > 0) {
                                    var suggestionUl = $('<ul>');
                                    $.each(resp.hits.hits, function (i, hit) {
                                        var li = $('<li>');
                                        if (i === 0) {
                                            li.addClass('first');
                                        }
                                        if (i === resp.hits.hits.length - 1) {
                                            li.addClass('last');
                                        }
                                        if (hit._source.title) {
                                            li.append('<span class="title">' + hit._source.title + '</span>');
                                        }
                                        li.append($('<div>').append(hit._source.content).text()).data('content', hit._source.content);
                                        li.on('click', function () {
                                            $('.control_panel .content #edit-area', container).html('');
                                            $('.control_panel .content #edit-area', container).focus();
                                            Tools.appendToEditDiv($(this).data('content'));
                                            $('.chat-suggestion', container).hide();
                                        });
                                        suggestionUl.append(li);
                                    });
                                    $('.chat-suggestion ul', container).remove().html('');
                                    $('.chat-suggestion', container).append(suggestionUl);
                                    $('.control_panel .content #edit-area', container).one('click', function () {
                                        $('.chat-suggestion', container).hide();
                                    });
                                    $('.chat-suggestion', container).show();
                                }
                            }, function (err) {
                                $F.log('Search error:', err);
                            }, 'chat', 1);
                        } else {
                            $('.chat-suggestion', container).hide();
                        }
                    }, 1000);
                });
            }

            $('.control_panel .content #edit-area', container).on('keyup', tools.debounce(function () {
                sendTypingNotice();
            }, 500, false));

            //注册表情按钮
            $('.chat_face', container).click(function (e) {
                e.stopPropagation();
                $('#mmpop_emoji_panel', container).toggle();
            });

            //是否显示默认qq表情与emoji表情
            if (CHAEMO === 1) {
                $('.chat .exp_hd .qq', container).hide();
                $('.chat .exp_bd .qq', container).hide();
                $('.chat .exp_hd .emoji', container).addClass('active');
                $('.chat .exp_bd .emoji', container).addClass('active');
            } else if (CHAEMO === 2) {
                $('.chat .exp_hd .emoji', container).hide();
                $('.chat .exp_bd .emoji', container).hide();
                $('.chat .exp_hd .qq', container).addClass('active');
                $('.chat .exp_bd .qq', container).addClass('active');
            } else if (CHAEMO === 3) {
                $('.chat .exp_hd .qq', container).hide();
                $('.chat .exp_bd .qq', container).hide();
                $('.chat .exp_hd .emoji', container).hide();
                $('.chat .exp_bd .emoji', container).hide();
            } else if (CHAEMO === 4) {
                //qq和emoji位置互换
                $('.chat .exp_hd .emoji', container).after($('.chat .exp_hd .qq', container));
                $('.chat .exp_bd .emoji', container).after($('.chat .exp_bd .qq', container));
                $('.chat .exp_hd .emoji', container).addClass('active');
                $('.chat .exp_bd .emoji', container).addClass('active');
                $('.chat .exp_hd .qq', container).removeClass('active');
                $('.chat .exp_bd .qq', container).removeClass('active');
            }

            //装载自定义表情
            $.getJSON('js/plugins/chat/custom-emoji.json').done(function (data) {
                if (data && data.length > 0) {
                    $('<li class="exp_hd_item" data-index="3"><a href="javascript:;">' + i18n.customEmoji + '</a></li>').appendTo($('.chat .exp_hd', container));
                    var cemos = '<div class="exp_cont active" data-index="3"><div class="custom_face">';
                    data.forEach(function (cemo) {
                        var cemoDisplayUrl = cemo.url;
                        if (!cemoDisplayUrl.toLowerCase().startsWith('http')) {
                            if (!cemoDisplayUrl.startsWith('/'))
                                cemoDisplayUrl = '/' + cemoDisplayUrl;
                            if (cemoDisplayUrl.startsWith(chatServerName)) {
                                cemoDisplayUrl = chatServerHost + cemoDisplayUrl;
                            } else {
                                cemoDisplayUrl = chatServerAddr + cemoDisplayUrl;
                            }
                        }
                        cemos += '<a type="custom" class="face customface" data-src="' + cemo.url + '"><img src="' + cemoDisplayUrl + '" /></a>';
                    });
                    cemos += '</div></div>';
                    $(cemos).appendTo($('.chat .exp_bd.scroll-content', container));
                    $('#mmpop_emoji_panel .custom_face a', container).click(function (e) {
                        e.stopPropagation();
                        $('.control_panel .content #edit-area', container).append('<img class="custom-emoji" data-src="' + $(this).attr('data-src') + '" src="' + $(this).find('img').attr('src') + '" >');
                    })
                }
            }).always(function () {
                //装载多表情tab
                $('.exp_hd_item', container).click(function (e) {
                    e.stopPropagation();
                    $('.exp_hd_item', container).removeClass('active');
                    $(this).addClass('active');
                    $('.exp_cont', container).removeClass('active');
                    $('.exp_cont[data-index=' + $(this).attr('data-index') + ']', container).addClass('active');
                });
            });

            //注册具体每个qq表情点击逻辑
            $('#mmpop_emoji_panel .qq_face a', container).click(function (e) {
                e.stopPropagation();
                var index = $(this).attr('class').replace('face qqface', '');
                var text = $(this).attr('title');
                $('.control_panel .content #edit-area', container).append('<img class="qqemoji qqemoji' + index + '" text="' + text + '" src="js/plugins/chat/images/spacer.gif" >');
            });
            //注册每个标准emoji表情点击逻辑
            $('#mmpop_emoji_panel .emoji_face a', container).click(function (e) {
                e.stopPropagation();
                var index = $(this).attr('class').replace('face emojiface', '');
                var text = $(this).attr('title');
                $('.control_panel .content #edit-area', container).append('<img class="standard-emoji standard-emoji' + index + '" text="' + text + '" src="js/plugins/chat/images/spacer.gif" >');
            });

            $.getJSON('js/plugins/chat/custom-icon.json').done(function (data) {
                c.customIcons = data;
            });

            //注册截图按钮
            $('.chat_screencut', container).click(function (e) {
                capture();
            });
            //初始化按钮的tooltip
            $('.chat_screencut', container).tooltip({
                show: {
                    delay: 500
                },
                content: '<div style="color:#343e4a;"><span style="font-weight: bold">' + i18n.capture + '</span><br><span style="font-size: 12px">' +
                    i18n.captureCurrentPage + '(alt+shift+a)</span><br><span style="font-size: 12px">' +
                    i18n.captureMinimumCurrentPage + '(alt+shift+z)</span><br><span style="font-size: 12px">' +
                    i18n.captureWin10Shortcut + '</span></div>'
            });

            //注册附件按钮
            $('.chat .chat_pic', container).click(function (e) {
                var form = $('.chat form#chat_upload_form'),
                    input = $("input[type='file']", form);

                input.off('change').on('change', function () {
                    var _fileName = $.trim(input.val());
                    if (!_fileName) { // 提交前的再次检测
                        //ie11里面会触发两次change，第二次文件名变成空时候也会触发一次，所以这里提示不需要了，空时候就不处理即可。
                        //$F.alert('没有选择上传的文件');
                    } else {
                        var csId = currentSessionId;
                        var sessionCur = sessionsMap["session" + currentSessionId];
                        let clientMessageId = '_message-' + new Date().getTime(),
                            $domMessage = '.chat .clearfix[data-state=' + clientMessageId + ']',
                            sendTime = 0;
                        var uploadRet = form.upload({
                            clearForm: true,
                            url: chatServerAddr + '/uploadh5',
                            upMaxSize: 0,
                            beforeSend: function () {
                                sendTime = setTimeout(function () {
                                    let message = addMessageToSession(csId, {
                                        id: c.configs.agentId,
                                        name: c.staff.displayName
                                    }, {
                                        stateId: clientMessageId,
                                        text: '<img src="js/plugins/chat/images/loading.gif"/>',
                                        translate: false
                                    }, Tools.getFormattedNowTime(true));
                                    message.revokable = true;
                                    appendTextToDiv(getDisplayMessage(message, sessionCur));
                                    $('.nickname', $domMessage).remove();
                                }, 1000)
                            },
                            complete: function () {
                                if (sendTime) clearTimeout(sendTime)
                                if ($($domMessage)[0]) {
                                    $($domMessage).remove();
                                }
                            },
                            showError: false,
                            allows: sessionCur.fileAcceptExtensions || c.configs.fileAcceptExtensions,
                            xhrFields: {
                                withCredentials: true
                            },
                            success: function (data) {
                                form[0].reset();
                                var handleUploadResponse = (data) => {
                                    if (data.status === 'success') {
                                        var attachURL = chatServerHost + data.url;
                                        var content, displayContent;
                                        var fName = data.name;
                                        if (!fName) { // 兼容老版本chat（这时候多文件上传会有问题）
                                            fName = _fileName.replace(/\\/gi, '/');
                                            fName = fName.substring(fName.lastIndexOf('/') + 1);
                                        }
                                        if ($F.isImage(fName)) {
                                            displayContent = '<img class="message-image" data-from="elite" src = "' + attachURL + '" title = "' + fName + '">';
                                            content = '<img data-from="elite" src = "' + data.url + '" title = "' + fName + '">';
                                        } else if (fName.toLowerCase().endsWith('.mp4')) {
                                            displayContent = '<video class="message-video" src="' + attachURL + '" data-url="' + data.url + '" data-name="' + fName + '" data-size="' + data.size + '" controls></video>';
                                            content = '<video src="' + data.url + '" data-name="' + fName + '" data-size="' + data.size + '" controls></video>';
                                        } else {
                                            displayContent = '<div class="message-file" data-url="' + attachURL + '" data-href="' + data.url + '" data-name="' + fName + '" data-size="' + data.size + '"></div>';
                                            // displayContent = '<div class="message-file" data-url="' + data.url + '" data-name="' + fName + '" data-size="' + data.size +'"></div>';
                                            content = '<a data-from="elite" href = "' + data.url + '" target = "_blank" class="attachment" data-size="' + data.size + '">' + fName + '</a>';
                                        }
                                        sendMessage(content, csId, displayContent);
                                    } else {
                                        $F.alert(i18n.error.uploadFileFailed + ': ' + data.message);
                                    }
                                };

                                if (data.files && data.files.length > 0) {
                                    data.files.forEach(d => {
                                        handleUploadResponse(d);
                                    })
                                } else {
                                    handleUploadResponse(data);
                                }
                            },
                            error: function () {
                                $F.alert(i18n.error.uploadFileFailed);
                            }
                        });
                        if (uploadRet.code < 0) {
                            $F.alert(i18n.error.uploadFileFailed + ': ' + uploadRet.message);
                        }
                    }
                }).click();
            });

            $('.chat_minimax_summary', container).click(function () {
                onSummaryChat()
            });

            $('.chat_minimax_optimization', container).click(function () {
                onOptimizationChat()
            });

            $('.chat_minimax_ai_response', container).click(function () {
                onAiResponseChat()
            });

            function onAiResponseChat() {
                const inputElement = $('.control_panel .content #edit-area', container)
                const body = document.querySelector('body');
                body.classList.add('custom-cursor');
                tools.miniMaxChat({
                    sessionId: currentSessionId,
                    askType: "suggest"
                }).then(minimaxRequest => {
                    if (minimaxRequest.status === 'success') {
                        let reply = minimaxRequest.result.reply || ''
                        try {
                            if (typeof reply === 'object') {
                                reply = JSON.stringify(reply)
                            }
                        } catch (e) { }
                        inputElement.text((inputElement.innerHTML || '') + reply)
                        const sessionCur = sessionsMap["session" + currentSessionId];
                        sessionCur.lastMinimaxMessage = reply
                    } else {
                        $F.err('Robot-Error')
                        console.log('Robot-Error', minimaxRequest)
                    }
                    body.classList.remove('custom-cursor');
                }).catch((e) => {
                    body.classList.remove('custom-cursor');
                    console.log(e)
                })
            }

            function onSummaryChat() {
                let select = null
                if (summaryParams.summaryOptions && summaryParams.summaryOptions.length) {
                    select = '<select class="chat_minimax_summary_select">' + summaryParams.summaryOptions.map((item, index) => {
                        return `<option key="` + item.key + `" ` + (summaryOption === index ? 'selected' : '') + ` >` + item.label + `</option>`
                    }).join('') + '</select>'
                }
                Helper.popover($('.chat_minimax_summary', container), $('<div class="chat_minimax_summary_popover"><div'), {
                    alignToInput: '',
                    stopClickPropagation: true,
                    blockSelfClick: true,
                    direction: 'north',
                    onOpen: function (closeFunc) {
                        if (summaryKey) {
                            $('.chat_minimax_summary_popover')[0].innerHTML = select + '<div class="chat_minimax_loading">loading...</div>'
                            tools.miniMaxChat({
                                sessionId: currentSessionId,
                                askType: "summary",
                                option: summaryKey
                            }).then(minimaxRequest => {
                                if (minimaxRequest.status === 'success' && minimaxRequest.result.reply) {
                                    let result = minimaxRequest.result.reply || ''
                                    try {
                                        if (typeof result === 'object') {
                                            result = JSON.stringify(result)
                                        }
                                    } catch (e) { }
                                    $('.chat_minimax_summary_popover')[0].innerHTML = select + '<div class="chat_minimax_summary_content">' + result.replace(/\n/g, '<br />') + '</div>'
                                } else {
                                    $('.chat_minimax_summary_popover')[0].innerHTML = select + '<div class="chat_minimax_error">Robot Error</div>'
                                }
                                $('.chat_minimax_summary_select').change(function (e) {
                                    summaryOption = summaryParams.summaryOptions.findIndex(function (item) {
                                        return item.label === e.target.value
                                    })
                                    if (summaryOption !== -1) {
                                        summaryKey = summaryParams.summaryOptions[summaryOption].key
                                    }
                                    closeFunc()
                                    onSummaryChat()
                                })
                            })
                        } else {
                            $('.chat_minimax_summary_popover')[0].innerHTML = select
                            $('.chat_minimax_summary_select').change(function (e) {
                                summaryOption = summaryParams.summaryOptions.findIndex(function (item) {
                                    return item.label === e.target.value
                                })
                                if (summaryOption !== -1) {
                                    summaryKey = summaryParams.summaryOptions[summaryOption].key
                                }
                                closeFunc()
                                onSummaryChat()
                            })
                        }
                    }
                })
            }

            function onOptimizationChat(refresh = false) {
                if (optimizationParams.refresh === 'every') {
                    // $('.chat_minimax_optimization_popover')[0].innerHTML = '<div class="chat_minimax_loading">loading...</div>'
                }
                const messageText = $('.control_panel .content #edit-area', container).text() || ""
                Helper.popover($('.chat_minimax_optimization', container), $('<div class="chat_minimax_optimization_popover"><div'), {
                    alignToInput: '',
                    stopClickPropagation: true,
                    blockSelfClick: true,
                    direction: 'north',
                    onOpen: function (closeFunc) {
                        $('.chat_minimax_optimization_popover')[0].innerHTML = '<div class="chat_minimax_loading">loading...</div>'
                        tools.miniMaxChat({
                            askType: "optimization",
                            text: messageText
                        }).then(minimaxRequest => {
                            if (minimaxRequest.status === 'success') {
                                let reply = minimaxRequest.result.reply
                                try {
                                    reply = JSON.parse(minimaxRequest.result.reply)
                                } catch (e) { }
                                if (Object.prototype.toString.call(reply) === '[object Array]') {
                                    optimizationArray = reply
                                } else {
                                    optimizationArray.push(reply)
                                }
                                $('.chat_minimax_optimization_popover')[0].innerHTML = '<div class="chat_minimax_optimization_content">' +
                                    optimizationArray.map((item) => {
                                        return `<div class="chat_minimax_optimization_item">` + item + `</div>`
                                    }).join('') + `<div class="chat_minimax_optimization_refresh">刷新</div>` + '</div>'
                                $('.chat_minimax_optimization_refresh').click(function () {
                                    onOptimizationChat(true)
                                })
                                $('.chat_minimax_optimization_item').click(function (e) {
                                    $('.control_panel .content #edit-area', container).text(e.target.innerHTML)
                                    closeFunc()
                                })
                            } else {
                                $('.chat_minimax_optimization_popover')[0].innerHTML = '<div class="chat_minimax_error">Robot Error</div>'
                            }
                        })
                    }
                })
            }

            $('.chat_push_rating', container).click(function () {
                var sessionCur = sessionsMap["session" + currentSessionId];
                if (sessionCur) {
                    var pr = function () {
                        callService({
                            data: {
                                requestType: 'pushRating',
                                sessionId: sessionCur.id
                            }
                        }).done(function (data) {
                            if (data && data.status === "success") {
                                sessionCur.hasPushedRating = true;
                                sessionCur.pushRatingCount++;
                                $('.chat_push_rating', container).addClass('rated');
                                var feedback = formatFeedbackMessage(i18n.feedback.hasPushedRatingMessage.format(sessionCur.pushRatingCount));
                                addMessageToSession(sessionCur.id, {
                                    id: "-1",
                                    name: null
                                }, {
                                    text: feedback,
                                    type: MESSAGE_TYPE.SYSTEM
                                }, undefined);
                                if (currentSessionId == sessionCur.id) {
                                    appendTextToDiv(feedback);
                                }
                            } else {
                                $F.err('Chat - pushRating', i18n.error.pushRatingFailed);
                            }
                        }).fail(function () {
                            $F.err('Chat - pushRating', i18n.error.pushRatingFailed);
                        });
                    };
                    if (pushRatingDirectly) {
                        pr();
                    } else {
                        Tools.confirm((sessionCur.pushRatingCount > 0 ? i18n.pushRatingConfirmHasPushed.format(sessionCur.pushRatingCount) : '') + i18n.pushRatingConfirm + '[' + sessionCur.client.name + ']', function () {
                            pr();
                        });
                    }
                }
            });

            //语言选择触发事件
            $("#trans_select").change(function (data) {
                if (translateAutoEnabled) {
                    var targetLang = $('.trans_to', container).val();
                    if (targetLang) {
                        setTranslateLanguage(currentSessionId, $.cookie('chat.translateTo') || 'zh', targetLang);
                    }
                }
            })

            $('.chat_translate', container).click(function () {
                var targetLang = $('.trans_to', container).val(),
                    content = $('.control_panel .content #edit-area', container).html();
                if (targetLang && targetLang.trim()) {
                    if (content && content.trim()) {
                        callService({
                            data: {
                                requestType: 'translate',
                                text: content,
                                targetLang: targetLang
                            }
                        }).done(function (data) {
                            if (data.status === "success") {
                                $('.control_panel .content #edit-area', container).html(data.text);
                            } else {
                                $F.err('Chat - translate', i18n.error.translateFailed + ': ' + data.message);
                            }
                        }).fail(function (e) {
                            $F.err('Chat - translate', i18n.error.translateFailed);
                        });
                    } else {
                        $F.notify(i18n.error.emptyInputNoNeedTranslate, 0);
                    }
                } else {
                    $F.notify(i18n.error.emptyTransforTarget, 0);
                }

            });


            //注册系统菜单逻辑
            $('.chat_add', container).click(function (e) {
                e.stopPropagation();
                clearTimeout(c.statusChangeHoverTimer);
                c.hideUserStatusMenu();
                $('#mmpop_system_menu', container).toggle();
                if (showSessionButtons === 1) {
                    $('.session_hd .title_wrap .session_btn_container .invite', container).show()
                    $('.session_hd .title_wrap .session_btn_container .transfer', container).show()
                    $('.session_hd .title_wrap .session_btn_container .transferToQueue', container).show()
                    $('#mmpop_system_menu .dropdown_menu .invite_li', container).show()
                    $('#mmpop_system_menu .dropdown_menu .transfer_li', container).show()
                    $('#mmpop_system_menu .dropdown_menu .transferToQueue_li', container).show()
                } else if (typeof showSessionButtons === "object" && showSessionButtons !== null) {
                    if (showSessionButtons.isInviteShow) {
                        $('.session_hd .title_wrap .session_btn_container .invite', container).show()
                        $('#mmpop_system_menu .dropdown_menu .invite_li', container).show()
                    } else {
                        $('.session_hd .title_wrap .session_btn_container .invite', container).hide()
                        $('#mmpop_system_menu .dropdown_menu .invite_li', container).hide()
                    }
                    if (showSessionButtons.isTransferShow) {
                        $('.session_hd .title_wrap .session_btn_container .transfer', container).show()
                        $('#mmpop_system_menu .dropdown_menu .transfer_li', container).show()
                    } else {
                        $('.session_hd .title_wrap .session_btn_container .transfer', container).hide()
                        $('#mmpop_system_menu .dropdown_menu .transfer_li', container).hide()
                    }
                    if (showSessionButtons.isTransferToQueueShow) {
                        $('.session_hd .title_wrap .session_btn_container .transferToQueue', container).show()
                        $('#mmpop_system_menu .dropdown_menu .transferToQueue_li', container).show()
                    } else {
                        $('.session_hd .title_wrap .session_btn_container .transferToQueue', container).hide()
                        $('#mmpop_system_menu .dropdown_menu .transferToQueue_li', container).hide()
                    }
                } else {
                    $('.session_hd .title_wrap .session_btn_container .invite', container).hide()
                    $('.session_hd .title_wrap .session_btn_container .transfer', container).hide()
                    $('.session_hd .title_wrap .session_btn_container .transferToQueue', container).hide()
                    $('#mmpop_system_menu .dropdown_menu .invite_li', container).hide()
                    $('#mmpop_system_menu .dropdown_menu .transfer_li', container).hide()
                    $('#mmpop_system_menu .dropdown_menu .transferToQueue_li', container).hide()
                }
            });

            // 添加坐席修改状态按钮
            $('#mmpop_system_menu .status-change', container).click(function (e) {
                e.stopPropagation();
                clearTimeout(c.statusChangeHoverTimer);
                $('.chat .user_status_menu', container).toggle();
            });
            $(".header .display_name .user_status", container).click(function (e) {
                e.stopPropagation();
                if (c.configs.allowAgentStatusChange === 'false') {
                    return;
                }
                if ($('#mmpop_system_menu', container).css('display') === 'none') {
                    if ($('.chat .user_status_menu', container).css('display') === 'none') {
                        $('.chat .user_status_menu', container).css('left', 20).show();
                    } else {
                        c.hideUserStatusMenu();
                    }
                }
            });
            $('#mmpop_system_menu .status-change', container).hover(function () {
                if ($('.chat .user_status_menu', container).css('display') === 'none') {
                    c.statusChangeHoverTimer = setTimeout(function () {
                        $('.chat .user_status_menu', container).show();
                    }, 500);
                } else {
                    clearTimeout(c.statusChangeHoverTimer);
                }
            }, function () {
                if ($('.chat .user_status_menu', container).css('display') === 'none') {
                    clearTimeout(c.statusChangeHoverTimer);
                } else {
                    c.statusChangeHoverTimer = setTimeout(function () {
                        c.hideUserStatusMenu();
                    }, 1000);
                }
            });

            //会议，转接，转接队列
            $('#mmpop_system_menu .invite, #mmpop_system_menu .transfer, #mmpop_system_menu .transferToQueue, ' +
                '.session_hd .title_wrap .session_btn_container .invite, ' +
                '.session_hd .title_wrap .session_btn_container .transfer, ' +
                '.session_hd .title_wrap .session_btn_container .transferToQueue', container).click(function (e) {
                    var curSession = sessionsMap["session" + currentSessionId];
                    if (!curSession) {
                        $F.alert(i18n.error.chooseOneSessionAlert);
                        return;
                    }
                    if (!curSession.active) {
                        $F.alert(i18n.error.sessionAlreadyClosedCantTransferAlert);
                        return;
                    }
                    if (curSession.permFlag == 1) {
                        $F.alert(i18n.error.permSessionCantTransferAlert);
                        return;
                    }
                    if ($(this).hasClass('invite')) {
                        $('#tcDialog .title', container).text(i18n.sendInvite);
                        currentTIR = 'agentInvite';
                        $('#tcDialog .agentname-input-container', container).show();
                    } else if ($(this).hasClass('transfer')) {
                        $('#tcDialog .title', container).text(i18n.sendTransfer);
                        currentTIR = 'agentTransfer';
                        $('#tcDialog .agentname-input-container', container).show();
                    } else {
                        $('#tcDialog .title', container).text(i18n.sendTransferToQueue);
                        currentTIR = 'agentTransferToQueue';
                        $('#tcDialog .agentname-input-container', container).hide();
                    }

                    getAgentListByQueueId(curSession.workgroupId, curSession.queueId, function () {
                        $('#tcDialog .chat-workgroup-select', container).val(curSession.workgroupId);
                        $('#tcDialog .chat-workgroup-select', container).change();
                        $('#tcDialog', container).show().css({
                            "paddingTop": ($(container).height() - $('#tcDialog .chat-dialog-content', container).height()) / 2
                        });
                        // $('#tcDialog .chat-queue-select', container).val(curSession.queueId).change();
                    });
                    $('#tcDialog .chat-queue-select', container).off().change(function () {
                        getAgentListByQueueId($('#tcDialog .chat-workgroup-select', container).val(), $(this).find('option:selected').val());
                    });
                });
            $('#tcDialog .chat-dialog-close', container).click(function () {
                $('#tcDialog', container).hide();
            });

            $('#tcDialog .confirm', container).click(function () {
                if (c.handlingTC) return;
                c.handlingTC = true;
                var comments = $('#tcDialog .comments .comments-editor', container).text();
                var sendInfo = {
                    actionType: currentTIR,
                    senderId: c.configs.agentId,
                    sessionId: currentSessionId,
                    comments: comments
                };
                if (currentTIR === 'agentTransferToQueue') {
                    var selectedQueueId = $('#tcDialog .chat-queue-select option:selected').val();
                    if (selectedQueueId == -1) {
                        c.handlingTC = false;
                        return $F.alert(i18n.plzChooseAQueue);
                    }
                    sendInfo.queueId = selectedQueueId;
                } else {
                    var selectedAgent = $("#tcDialog .contact_item:has(i.web_wechat_choose_green)", container);
                    if (selectedAgent.length === 0) {
                        c.handlingTC = false;
                        $F.alert(i18n.plzChooseAnAgent);
                        return;
                    }
                    var receiverId = selectedAgent.attr('data-id');
                    sendInfo.receiverId = receiverId;
                    var selectedQueueId = $('#tcDialog .chat-queue-select option:selected').val();
                    if (selectedQueueId != -1) {
                        sendInfo.queueId = selectedQueueId;
                    }
                }
                $('#tcDialog .confirm', container).addClass('loading');
                sendTCRequest(sendInfo).then(() => {
                    $('#tcDialog .confirm', container).removeClass('loading');
                    c.handlingTC = false;
                });
            });

            $('.closeSessionDialog .chat-dialog-close', container).click(function () {
                $('.closeSessionDialog', container).hide();
            });

            //个性配置
            $('#mmpop_system_menu .cog', container).click(function () {
                $('.configDialog', container).show().css({
                    "paddingTop": ($(container).height() - $('.configDialog .chat-dialog-content', container).height()) / 2
                });
            });
            $('.leftPanelColor', container).change(function () {
                $('.left_panel', container).css('background-color', $(this).val());
                $.cookie('chat.leftpanel.bgcolor', $(this).val(), {
                    expires: 365
                });
            });
            //注册"聊天面板放到左侧"的单选框事件，重新构建chatPanel
            $('.configDialog .swapRL', container).change(function () {
                var swapRL = $(this).is(':checked') ? 'right' : 'left';
                $.cookie('chat.swapRL', swapRL, {
                    expires: 365
                });
                $E.openChatPanel(false, true);
            });
            //注册"是否开启输入联想"单选框事件
            $('.configDialog .suggestionEnabled', container).change(function () {
                customSuggestionEnabled = $(this).is(':checked');
                $.cookie('chat.suggestionEnabled', customSuggestionEnabled, {
                    expires: 365
                });
            });
            //注册"是否开启新消息置顶"单选框
            $('.configDialog input[name=topSessionOnNewMessage]', container).change(function () {
                topSessionOnNewMessage = $('.configDialog input[name=topSessionOnNewMessage]:checked').val();
                $.cookie('chat.topSessionOnNewMessage', topSessionOnNewMessage, {
                    expires: 365
                });
            });

            function reloadDirtyWords() {
                dirtyWordsScanEnabled = $('.configDialog .dirtyWordsScanEnabled', container).is(':checked');
                $.cookie('chat.dirtyWordsScanEnabled', dirtyWordsScanEnabled, {
                    expires: 365
                });
                if (dirtyWordsScanEnabled) {
                    //加载脏词库
                    callService({
                        data: {
                            requestType: 'loadDirtyWords'
                        }
                    }).done(function (data) {
                        if (data.status === "success") {
                            c.dirtyWords = data.dirtyWords;
                        }
                    }).fail(function () {
                        $F.err('Chat - loadDirtyWords', i18n.error.loadDirtywordsFailed);
                    });
                }
            }

            //注册"是否开启敏感词提示"单选框
            $('.configDialog .dirtyWordsScanEnabled', container).change(function () {
                reloadDirtyWords();
            });

            //注册"是否开启辅助机器人自动回复"单选框
            $('.configDialog .robotAutoAnswerEnabled', container).change(function () {
                robotAutoAnswerEnabled = $(this).is(':checked');
                $.cookie('chat.robotAutoAnswerEnabled', robotAutoAnswerEnabled, {
                    expires: 365
                });
            });

            //注册"是否开启辅助机器人自动回复"单选框
            $('.configDialog .translateAutoEnabled', container).change(function () {
                translateAutoEnabled = $(this).is(':checked');
                if (!translateAutoEnabled) {
                    Object.keys(sessionsMap).forEach(function (key) {
                        setTranslateLanguage(sessionsMap[key].id, '', '')
                    });
                }
                $.cookie('chat.translateAutoEnabled', translateAutoEnabled, {
                    expires: 365
                });
            });
            //注册"网聊新消息通知是否开启声音"单选框
            $('.configDialog .newMassageSoundSwitch', container).change(function () {
                newMassageSoundEnabled = $(this).is(':checked');
                $.cookie('chat.newMassageSoundEnabled', newMassageSoundEnabled, {
                    expires: 365
                });
            });


            //注册"是否直接推送满意度"单选框
            $('.configDialog .pushRatingDirectly', container).change(function () {
                pushRatingDirectly = $(this).is(':checked');
                $.cookie('chat.pushRatingDirectly', pushRatingDirectly, {
                    expires: 365
                });
            });

            //注册"翻译目标语言"下拉框
            $('.configDialog .transToConfig', container).change(function () {
                $.cookie('chat.translateTo', $(this).val(), {
                    expires: 365
                });
                if (translateAutoEnabled) {
                    let agentLanguage = $(this).val();
                    //对所接收到的语言进行设置
                    Object.keys(sessionsMap).forEach(function (key) {
                        setTranslateLanguage(sessionsMap[key].id, agentLanguage, sessionsMap[key].languageTo)
                    });
                }
            });


            $('.configDialog .chat-dialog-close', container).click(function () {
                $('.configDialog', container).hide();
            });


            //退出
            $('#mmpop_system_menu .sign-out', container).click(function () {
                var findActiveSession = false;
                $.each(sessionsMap, function (key, s) {
                    if (s.permFlag != 1 && s.active) {
                        findActiveSession = true;
                        return false;
                    }
                });
                if (findActiveSession) {
                    return $F.alert(i18n.error.closeSessionsBeforeLoggedOut);
                }
                if (userStatusTimer)
                    clearTimeout(userStatusTimer);
                c.logoutAndClose();
            });

            $('.requestDialog .chat-dialog-close', container).click(function () {
                $('.requestDialog', container).hide();
            });

            $('.message_preview_btn', container).click(function () {
                var msg = $('.control_panel .content #edit-area', container).html(); // 可能需要改成text()，albert说这里用html会出现转义，可能造成消息发不出去
                if (msg) {
                    c.previewFormattedMessage(msg);
                }
            });
            $('.messagePreviewDialog .confirm', container).click(function () {
                $('.messagePreviewDialog', container).hide();
                var $this = $(this);
                sendMessage($this.data('msg'), $this.data('sessionId'), undefined, undefined, function () {
                    $('.control_panel .content #edit-area', container).html("");
                });
            });
            $('.messagePreviewDialog .chat-dialog-close', container).click(function () {
                $('.messagePreviewDialog', container).hide();
            });

            $('.session_more_menu .stick', container).click(function () {
                let sessionId = $('.session_more_menu', container).data('sessionId');
                let target = $('#sessionTab_' + sessionId, container);
                if (target.hasClass('top')) {
                    if (target.hasClass('closed')) {
                        $('.session_list .chat_item', container).last().after(target);
                    } else {
                        if ($('.session_list .chat_item:not(.top)', container).length > 0) {
                            $('.session_list .chat_item:not(.top)', container).first().before(target);
                        } else {
                            $('.session_list .chat_item.top', container).last().after(target);
                        }
                    }
                    $(target).removeClass('top');
                } else {
                    if ($('.session_list .chat_item.top.closed', container).length > 0) {
                        if (target.hasClass('closed')) {
                            $('.session_list .chat_item.top.closed', container).last().after(target);
                        } else {
                            $('.session_list .chat_item.top.closed', container).first().before(target);
                        }
                    } else if ($('.session_list .chat_item.top', container).length > 0) {
                        $('.session_list .chat_item.top', container).last().after(target);
                    } else {
                        $('.session_list .chat_item', container).first().before(target);
                    }
                    $(target).addClass('top');
                }
            });

            $('.session_more_menu .lock', container).click(function () {
                let sessionId = $('.session_more_menu', container).data('sessionId');
                let target = $('#sessionTab_' + sessionId, container);
                if (target.hasClass('locked')) {
                    $(target).removeClass('locked');
                    sessionsMap["session" + sessionId].locked = false;
                } else {
                    $(target).addClass('locked');
                    sessionsMap["session" + sessionId].locked = true;
                }
            });

            /**
             * 加载cookie配置
             */
            // 加载左侧chatItem背景色
            var leftPanelBgColor = $.cookie("chat.leftpanel.bgcolor");
            if ($F.isColorStr(leftPanelBgColor)) {
                $('.left_panel', container).css('background-color', leftPanelBgColor);
            }
            //加载显示在左还是右
            if ($.cookie('chat.swapRL') === 'right') {
                $('.configDialog .swapRL', container).prop('checked', true);
            }
            //加载是否开启建议提示
            if (!suggestionSearchEnabled) { // 如果系统配置就不开启建议搜索，则不显示个性配置中的是否开启
                $('.configDialog .suggestionEnabledContainer', container).hide();
            } else { // 如果系统开启，则考虑cookie，来设置自定义是否开启
                customSuggestionEnabled = $.cookie('chat.suggestionEnabled') === 'true';
                $('.configDialog .suggestionEnabled', container).prop('checked', customSuggestionEnabled);
            }
            //加载是否新消息来置顶
            topSessionOnNewMessage = $.cookie('chat.topSessionOnNewMessage');
            $('.configDialog input[name=topSessionOnNewMessage][value=' + topSessionOnNewMessage + ']', container).attr('checked', 'checked');
            //加载敏感词过滤
            dirtyWordsScanEnabled = $.cookie('chat.dirtyWordsScanEnabled') === 'true';
            $('.configDialog .dirtyWordsScanEnabled', container).prop('checked', dirtyWordsScanEnabled);
            //加载辅助机器人自动应答
            robotAutoAnswerEnabled = $.cookie('chat.robotAutoAnswerEnabled') === 'true';
            $('.configDialog .robotAutoAnswerEnabled', container).prop('checked', robotAutoAnswerEnabled);
            //加载自动翻译的配置
            translateAutoEnabled = $.cookie('chat.translateAutoEnabled') === 'true';
            $('.configDialog .translateAutoEnabled', container).prop('checked', translateAutoEnabled);
            //加载来新消息是否声音提醒
            newMassageSoundEnabled = $.cookie('chat.newMassageSoundEnabled') === 'true';
            $('.configDialog .newMassageSoundSwitch', container).prop('checked', newMassageSoundEnabled);
            //加载是否开启直接推送满意度
            pushRatingDirectly = $.cookie('chat.pushRatingDirectly') === 'true';
            $('.configDialog .pushRatingDirectly', container).prop('checked', pushRatingDirectly);
            $('.configDialog .transToConfig', container).val($.cookie('chat.translateTo') || 'zh');
            sendType = $.cookie("chat_plugin_send_type") || '0'
            $('.control_panel .action .desc', container).text($('.control_panel .send_switcher_ul li[data-type="' + sendType + '"]').text())

            //初始化请求tabs
            requestTabs = $('.requestDialog .requestTabs', container).tabs({
                heightStyle: "fill"
            });

            //事件注册
            /**
             * 添加消息到输入框, 可以让动态页面去管理预存消息，通过这个事件发送给chat模块
             */
            project.events.register($CONST.ChatEvent.ADD_MESSAGE_TO_TEXTAREA, 'chat', function (data) {
                var content = data.content;
                if (content) {
                    var curSession = sessionsMap["session" + currentSessionId];
                    if (curSession && curSession.active) {
                        if (data.raw) {
                            $('.control_panel .content #edit-area', container).text(content);
                        } else {
                            if (!data.nobr)
                                content = content.replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\r/g, '<br/>');
                            $('.control_panel .content #edit-area', container).html(content);
                        }
                    }
                }
            });


            //刷新用户列表
            project.events.register('RELOAD_DIRTY_WORDS', 'chat', function (data) {
                reloadDirtyWords();
            });


            //刷新用户列表
            project.events.register('UPDATE_PERM_SESSIONS', 'chat', function (data) {
                if (data.sessionId && sessionsMap["session" + data.sessionId]) {
                    sessionsMap["session" + data.sessionId].active = false;
                }
                updatePermSessions(function () {
                    $F.notify(i18n.refreshUserSuccess);
                });
            });

            /**
             * 批量添加消息内容到聊天界面
             * 添加消息到输入框, 可以让动态页面去管理预存消息，通过这个事件发送给chat模块
             */
            project.events.register($CONST.ChatEvent.BATCH_ADD_MESSAGE_TO_TAB_SESSION, 'chat', function (data) {
                var messages = data.messages;
                if (messages) {
                    $.each(messages, function (index, message) {
                        var currentSessionId = message.sessionId;
                        var curSession = sessionsMap["session" + currentSessionId];
                        if (curSession && curSession.active) {
                            var text = message.content;
                            if (text) {
                                addMessageToSession(sessionId, {
                                    id: c.configs.agentId,
                                    name: c.staff.displayName
                                }, {
                                    text: text
                                }, Tools.getFormattedTime(message.postTime));
                            }
                        }
                    });

                }
            });
            /**
             * 关闭会话消息，收到此消息后，关闭对应会话
             */
            project.events.register($CONST.ChatEvent.CLOSE_SESSION, 'chat', function (data) {
                var sessionId = parseInt(data.sessionId);
                var comments = data.comments || '';
                var callNatures = data.callNatures || '';
                removeTabAndSession(sessionId, callNatures, comments);
            });

            /**
             * 结束会话消息，收到此消息后，结束对应会话，但是不会关闭tab
             */
            project.events.register($CONST.ChatEvent.END_SESSION || 'END_SESSION', 'chat', function (data) {
                var sessionId = parseInt(data.sessionId);
                var comments = data.comments || '';
                var callNatures = data.callNatures || '';
                endSession(sessionId, callNatures, comments);
            });

            /**
             * 改变客户，动态页面可以发出这个消息来改变当前聊天客户
             */
            project.events.register($CONST.ChatEvent.CHANGE_CLIENT, 'chat', function (data) {
                var sessionId = data.sessionId;
                var clientId = data.clientId;
                var session = sessionsMap["session" + sessionId];
                if (session) {
                    //调用changeCustomer接口，修改会话中的客户
                    callService({
                        data: {
                            requestType: 'changeCustomer',
                            sessionId: sessionId,
                            customerGuid: clientId
                        }
                    }).done(function (data) {
                        $F.log('Chat - CHANGE_CLIENT DATA', data);
                        if (data.status === "success") {
                            updateClientForSession(session, clientId, data.customerName);
                            switchTabAndSession('sessionTab_' + sessionId);
                        } else {
                            $F.err('Chat - changeCustomer', 'Change customer failed: ' + clientId);
                        }
                    }).fail(function () {
                        $F.err('Chat - changeCustomer', 'Change customer failed: ' + clientId);
                    });
                } else {
                    $F.log('Chat - CHANGE_CLIENT', 'No session found: ' + sessionId);
                }
            });

            /**
             * 注册电话客户确认事件，确认后，也作为一个聊天客户显示在tab上
             */
            project.events.register($CONST.ChatEvent.PHONE_CUSTOMER_CONFIRMED, 'chat', function (customer) {
                var PHONE_SESSION_ID = -100,
                    PHONE_TAB_ID = 'sessionTab_' + PHONE_SESSION_ID;
                var newSession = createSession(PHONE_SESSION_ID, customer, -1, '', {});
                sessionsMap["session" + PHONE_SESSION_ID] = newSession;

                var tab = $('#' + PHONE_TAB_ID, container);
                if (tab.length === 0) {
                    sessionIdsArr[sessionIdsArr.length] = PHONE_SESSION_ID;
                    var chatItem = $('<div id="' + PHONE_TAB_ID + '" data="' + PHONE_SESSION_ID + '" class="chat_item">' +
                        '<div class="ext">' +
                        '<p class="attr"></p>' +
                        '</div>' +
                        '<div class="avatar">' +
                        '<img class="img" src="' + (customer.icon || 'js/plugins/chat/images/customer.png') + '">' +
                        '</div>' +
                        '<div class="info">' +
                        '<h3 class="nickname">' +
                        '<span class="nickname_text">[' + i18n.phone + ']' + customer.customername + '</span>' +
                        '</h3>' +
                        '</div>' +
                        '</div>');
                    chatItem.click(function () {
                        switchTabAndSession(PHONE_TAB_ID);
                    });
                    chatItem.appendTo($('.session_list', container));
                }
                topSession(PHONE_SESSION_ID);
            });

            /**
             * 更新某个会话的客户的标签
             */
            project.events.register($CONST.ChatEvent.UPDATE_CLIENT_TAGS, 'chat', function (data) {
                var chatItem = $('#sessionTab_' + data.sessionId, container);
                if (chatItem.length > 0 && data.tags) {
                    $('.tags', chatItem).empty();
                    var tagsContainer = $('.tags', chatItem);
                    $.each(data.tags, function (i, t) {
                        tagsContainer.append('<span style="background-color:' + t.bgColor + ';color:' + t.color + '">' + t.comments + '</span>');
                    });
                }
            });

            /**
             * 添加自定义信息到客户的tab上
             */
            project.events.register($CONST.ChatEvent.ADD_CUSTOMINFO_ON_CHATITEM, 'chat', function (data) {
                var chatItem = $('#sessionTab_' + data.sessionId, container);
                if (chatItem.length > 0 && data.customInfo) {
                    $('.customInfo', chatItem).remove();
                    var customInfoContainer = $('<div class="customInfo">');
                    customInfoContainer.append(data.customInfo);
                    chatItem.append(customInfoContainer);
                }
            });

            /**
             * 更新某个会话的客户的评级
             */
            project.events.register($CONST.ChatEvent.UPDATE_CLIENT_RATE, 'chat', function (data) {
                var chatItem = $('#sessionTab_' + data.sessionId, container);
                if (chatItem.length > 0 && !isNaN(data.rating)) {
                    $('.rate', chatItem).rateYo("rating", data.rating);
                }
            });

            /**
             * 拉黑某个客户，需要传递sessionId
             */
            project.events.register($CONST.ChatEvent.BLOCK_USER, 'chat', function (data) {
                callService({
                    data: {
                        requestType: 'blockUser',
                        sessionId: data.sessionId,
                        comment: data.comment
                    }
                }).done(function (data) {
                    if (data.status === 'success') {
                        $F.log('Chat - blockUser', i18n.blockUser + ' [' + data.clientId + ' ]' + i18n.success);
                        $F.notify(i18n.blockUser + ' [' + data.clientId + '] ' + i18n.success);
                    } else {
                        $F.err('Chat - blockUser', i18n.error.blockUserFailed, data);
                    }
                }).fail(function () {
                    $F.err('Chat - blockUser', i18n.error.blockUserFailed);
                });
            });

            /**
             * 直接发送消息, 如果传递noticeType，则把消息作为通知类型消息发送，不然则作为普通消息发送
             * {
             sessionId: 1,  	//会话id
             content: '消息内容', //消息内容
             noticeType: 99, 	//通知消息类型(可选)
             success: function(data){ //发送成功回调
             console.log(data);
             }
             }
             */
            project.events.register($CONST.ChatEvent.SEND_MESSAGE, 'chat', function (data) {
                $F.log('Chat - sendMessage', JSON.stringify(data));
                var session = sessionsMap["session" + data.sessionId];
                if (session && session.active) {
                    if (data.noticeType) {
                        callService({
                            data: {
                                requestType: 'send',
                                data: getPostNoticeMessageSentInfo(data.content, data.sessionId, data.noticeType, data.receiverId)
                            }
                        }).done(function (data) {
                            if (data.noticeType != NOTICE_TYPE.TYPING && data.noticeType != NOTICE_TYPE.CUSTOM) {
                                if (data.content) {
                                    if (data.sessionId == currentSessionId) {
                                        appendTextToDiv(formatFeedbackMessage(data.content));
                                    }
                                    addMessageToSession(data.sessionId, {
                                        id: "-1",
                                        name: null
                                    }, {
                                        text: formatFeedbackMessage(data.content),
                                        type: MESSAGE_TYPE.SYSTEM
                                    }, undefined);
                                }
                            }
                            if ($.isFunction(data.success)) {
                                data.success(data);
                            }
                        }).fail(function () {
                            $F.err('Chat -sendMessage', i18n.error.sendMessageFailed);
                        });
                    } else {
                        if (data.content) { // 通知类消息可以没有content，但是普通消息必须要有
                            data.content = data.content.replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\r/g, '<br/>');
                            //禁用词过滤
                            if (chatSensitiveSend && c.forbidWords) {
                                var forbidWord = false,
                                    alertForbidContent = data.content;
                                c.forbidWords.forEach(function (dw) {
                                    if (data.content.indexOf(dw) > -1) {
                                        forbidWord = true;
                                        var dwReg = new RegExp(dw, 'g');
                                        alertForbidContent = alertForbidContent.replace(dwReg, '<span class="dw-highlight">' + dw + '</span>');
                                    }
                                });
                                if (forbidWord) {
                                    return Tools.confirmPrompt(i18n.prohibitSendingProhibitedWords + ': \n<div>' + alertForbidContent + '</div>', function () { });
                                } else {
                                    sendMessage(data.content, data.sessionId, data.content, data.extra, data.success);
                                }
                            }
                        }
                    }
                }
            });

            //切换到某个会话
            project.events.register($CONST.ChatEvent.SWITCH_TO_SESSION || 'SWITCH_TO_SESSION', 'chat', function (data) {
                var session = sessionsMap["session" + data.sessionId];
                if (session) {
                    switchTabAndSession('sessionTab_' + data.sessionId);
                } else {
                    $F.alert(i18n.error.cannotSwitchToSession + ': ' + data.sessionId);
                }
            });

            //坐席主动与某个客户开始会话
            project.events.register($CONST.ChatEvent.START_SESSION || 'START_SESSION', 'chat', function (data) {
                var clientId = data.clientId;
                var findActiveSession = false;
                $.each(sessionsMap, function (key, s) {
                    if (s.clientId === clientId && s.active) {
                        // 如果找到已经在聊天中会话，直接切换过去
                        switchTabAndSession('sessionTab_' + s.id);
                        findActiveSession = true;
                    }
                });
                // 坐席发起一个会话
                if (!findActiveSession) {
                    callService({
                        data: {
                            requestType: "agentStartSession",
                            clientId: clientId,
                            sessionType: data.st,
                            serviceAccountId: data.serviceAccountId || 'main',
                            perm: data.perm || 0,
                            queueId: data.queueId || 0
                        }
                    }).done(function (resp) {
                        if (resp.status === 'success') {
                            if (resp.result.code === 1) {
                                //创建会话
                                var sessionId = resp.result.sessionId,
                                    historySessions = resp.result.historySessions,
                                    client = resp.result.client,
                                    queueId = resp.result.queueId;
                                var newSession = createSession(sessionId, client, queueId, '', {});
                                if (resp.result.perm)
                                    newSession.permFlag = 1;
                                sessionsMap["session" + sessionId] = newSession;
                                sessionIdsArr[sessionIdsArr.length] = sessionId;
                                lastCreatedSessionId = "session" + sessionId;
                                if (historySessions) {
                                    $.each(historySessions, function (i, hs) {
                                        $.each(hs.newMessages, function (j, message) {
                                            var text = message.text;
                                            if (!Robot.isRobot(message.userId)) { //非机器人时候才需要parse
                                                text = tools.parseDisplayContent(text);
                                            } else {
                                                text = Robot.parseRobotMessage(text, message.userId);
                                            }
                                            if (text) {
                                                try {
                                                    message.reference = JSON.parse(message.reference)
                                                } catch (e) { }
                                                if (message.messageType === NOTICE_TYPE.CUSTOM) {
                                                    addMessageToSession(sessionId, {
                                                        id: "-1",
                                                        name: null
                                                    }, {
                                                        text: formatFeedbackMessage(message.text),
                                                        type: MESSAGE_TYPE.SYSTEM
                                                    });
                                                } else {
                                                    addMessageToSession(sessionId, {
                                                        id: message.userId,
                                                        name: message.userName,
                                                        icon: message.icon
                                                    }, {
                                                        id: message.revokeId || message.id,
                                                        text: text,
                                                        translate: message.translateMesssage,
                                                        revokeFlag: message.revokeFlag,
                                                        history: true,
                                                        revokeId: message.revokeId,
                                                        quoteMessageText: (message.reference && message.reference.content) ? message.reference.content.displayText : undefined,
                                                        quoteMessageId: message.reference ? message.reference._id : undefined,
                                                        quoteMessageSender: (message.reference && message.reference.user) ? message.reference.user.name : ''
                                                    }, Tools.getFormattedTime(message.postTime));
                                                }
                                            }
                                        });
                                        var sessionEndTime = '';
                                        if (hs.sessionInfo && hs.sessionInfo.endTime) {
                                            sessionEndTime = ' [' + new Date(hs.sessionInfo.endTime).format('yyyy-MM-dd hh:mm:ss') + ']';
                                        }
                                        addMessageToSession(sessionId, {
                                            id: "-1",
                                            name: null
                                        }, {
                                            text: '<div class="notice"><span class="line">————————</span>会话' + hs.id + sessionEndTime + '<span class="line">————————</span></div>',
                                            history: true,
                                            type: MESSAGE_TYPE.SYSTEM
                                        });
                                    });
                                }
                                setupClientDisplayName(client);
                                setupClientIcon(client);
                                createNewTab('sessionTab_' + sessionId, client);
                                switchTabAndSession('sessionTab_' + sessionId);
                            } else {
                                $F.alert(resp.result.message);
                            }
                        }
                    }).fail(function () {
                        $F.alert(i18n.error.createSessionFailed + ' ajax fail');
                    });
                }
            });

            /**
             * 动态页面自己查询出某个坐席(通过调用getNonOfflineAgentList请求来获取在线坐席)，然后发送消息过来触发转接会议请求
             * 参数包括：
             *    {
             *        actionType: 'agentTransfer', // agentTransfer 转接，agentInvite 会议， agentTransferToQueue 转接到组
             *        sessionId： 1023, // 会话id
             *        comments: 'xxxx备注', // 备注消息，会发送给对方坐席看到
             *        receiverId: 'SELITE', // 转接或者会议给的坐席id
             *        queueId: 1 // 如果是转接到组，则要传递队列id
             *    }
             * 对于获取在线坐席的接口getNonOfflineAgentList，调用方式为：
             *    POST: $project.getModuleInstance(window.$CONST.ModuleName.CHAT).getNonOfflineAgentList(queueId, sessionId).done(function(data){
             *      if (data.status === "success") {
             console.log(JSON.stringify(data.agentList));
             }
             *  });
             *    其中sessionId是你要发起转接会议的会话id。
             *    queueId为你要获取的某个队列下的坐席，如果传递-1表示获取所有队列中在线坐席
             */
            project.events.register('SEND_TC_REQUEST', 'chat', function (data) {
                if (!data.sessionId || data.sessionId < 0) {
                    return $F.log('Chat - sendTCRequest', 'No session id specified.');
                }
                var sendInfo = {
                    actionType: data.actionType,
                    senderId: c.configs.agentId,
                    sessionId: data.sessionId,
                    comments: data.comments
                };
                if (data.actionType === 'agentTransferToQueue') {
                    if (!data.queueId || data.queueId === -1) {
                        return $F.log('Chat - sendTCRequest', 'No queue id specified.');
                    }
                    sendInfo.queueId = data.queueId;
                } else {
                    if (!data.receiverId) {
                        return $F.log('Chat - sendTCRequest', 'No receiver id specified.');
                    }
                    sendInfo.receiverId = data.receiverId;
                }
                sendTCRequest(sendInfo);
            });

            /**
             * 动态页面发起坐席主动接起某个请求的事件监听
             */
            project.events.register('PICK_UP_REQUEST', 'chat', function (data) {
                var sentInfoJSON = JSON.stringify({
                    actionType: "pickUpRequest",
                    senderId: c.configs.agentId,
                    chatRequestId: data.requestId
                });

                $F.log("Chat - pick up request", sentInfoJSON);
                callService({
                    data: {
                        requestType: "send",
                        data: sentInfoJSON
                    }
                }).done(function (data) {
                    if (data.status === 'success') {
                        var sendResponse = data.sendResponse;
                        if (sendResponse) {
                            var result = sendResponse.result;
                            if (result !== 1) {
                                var feedbackMessage = sendResponse.feedbackMessage;
                                $F.log('Chat - pick up request', "Pick up failed: " + feedbackMessage);
                                $F.notify(i18n.error.pickupRequestFailed, 0);
                            } else {
                                processRespondToRequestReturn(data);
                            }
                        } else {
                            $F.notify(i18n.error.pickupRequestFailed, 0);
                        }
                    } else {
                        $F.notify(i18n.error.pickupRequestFailed, 0);
                    }
                }).fail(function (xhr) {
                    $F.notify(i18n.error.pickupRequestFailed, 0);
                    $F.log('Chat - pick up request', "Pick up ajax failed: " + xhr.status + ' ' + xhr.statusText);
                });
            });

            if (!project.chatSwitch) {
                project.chatSwitch = project.rightPanelSwitch;
            }
            //点击chat按钮删除提示数字
            project.chatSwitch.click(function (e) {
                e.preventDefault();
                $('i.web_wechat_reddot_middle', project.chatSwitch).remove();
            });

            //初始数据加载
            async.series([
                //do login
                function (callback) {
                    callService({
                        url: "agentService.do",
                        data: {
                            requestType: "login",
                            token: c.project.token,
                            lang: $E.lang,
                            ds: $E.dataService.key
                        }
                    }).done(function (data) {
                        if (data.status == 1) {
                            c.staff = data.staff;
                            c.logined = true;
                            c.currentUserStatus = c.staff.status || USER_STATUS.ONLINE;
                            c.staff.displayName = c.staff.alias || c.staff.name;
                            $(".left_panel .header .name_top .name", container).html(c.staff.displayName);
                            //通知框架，chat已经登录
                            notifyProjectEvent($CONST.ChatEvent.CHAT_LOGINED, {})
                            $('.c_chat i').css('color', '#00bfa6')
                            callback(null, "login");

                            // 展示坐席辅助小浮屏
                            if (showAgentAssistIframe && showAgentAssistIframe.id && showAgentAssistIframe.url) {
                                H5Utils.initChatBox(showAgentAssistIframe.url, showAgentAssistIframe.txt, showAgentAssistIframe.imageUrl, showAgentAssistIframe.id, showAgentAssistIframe.style);
                            }
                        } else {
                            callback(data.message);
                        }
                    }).fail(function (xhr) {
                        $F.alert(i18n.error.loggedInChatFailed);
                        callback("login ajax failed: " + xhr.status + ' ' + xhr.statusText, "login");
                    });
                },

                function (callback) {
                    if (enableCrossWorkgroupTransfer) {
                        $('#tcDialog .chat-workgroup-select', container).show();
                    }
                    // 获取所有工作组
                    callService({
                        data: {
                            requestType: 'getAllWorkgroupsAndQueues'
                        }
                    }).done(function (data) {
                        if (data.status === "success") {
                            c.workgroups = data.workgroups;
                            $.each(c.workgroups, function (i, workgroup) {
                                $('#tcDialog .chat-workgroup-select', container).append('<option value=' + workgroup.id + '>' + workgroup.name + '</option>');
                            });
                            $('#tcDialog .chat-workgroup-select', container).change(function () {
                                $('#tcDialog .chat-queue-select', container).empty();
                                var workgroupId = parseInt(this.value);
                                var selectedWorkgroup = c.workgroups.filter(function (w) {
                                    return w.id === workgroupId;
                                });
                                if (selectedWorkgroup && selectedWorkgroup.length >= 1) {
                                    selectedWorkgroup = selectedWorkgroup[0];
                                    if (currentTIR !== 'agentTransferToQueue') {
                                        $('#tcDialog .chat-queue-select', container).append('<option value="-1">' + i18n.all + '</option>');
                                    }
                                    $.each(selectedWorkgroup.queues, function (i, queue) {
                                        $('#tcDialog .chat-queue-select', container).append('<option value=' + queue.id + '>' + queue.name + '</option>');
                                    });
                                    $('#tcDialog .chat-queue-select', container).change();
                                }
                            });

                            $('#tcDialog .agentname-input', container).keyup(function () {
                                var filterName = $(this).val();
                                var filteredAgentList = c.agentList.filter(function (agent) {
                                    var agentName = agent.name || agent.firstName;
                                    return agentName.indexOf(filterName) > -1;
                                });
                                setupAgentsForTC($('#tcDialog .contacts', container), filteredAgentList, filterName);
                            });
                        } else {
                            $F.err('Chat - getAllWorkgroupsAndQueues', i18n.error.loadQueueAndWorkgroupFailed, data);
                        }
                        callback(null, 'workgroups');
                    }).fail(function () {
                        $F.err('Chat - getAllWorkgroupsAndQueues', i18n.error.loadQueueAndWorkgroupFailed);
                        callback("get workgroups ajax failed", 'workgroups');
                    });
                },

                //get config
                function (callback) {
                    //获取配置信息
                    callService({
                        data: {
                            requestType: "getAgentConfigurations"
                        }
                    }).done(function (data) {
                        if (data && data.status === "success") {
                            c.configs = data.agentConfigs;
                            $F.log('Chat - init', 'agentConfigurations: ' + JSON.stringify(c.configs));

                            if (!c.configs.isAgentInWorkgroup) {
                                $F.alert(i18n.error.notAssignedAQueue);
                            }
                            //如果存在持久会话，则直接显示相关会话
                            var permSessions = c.configs.permSessions;
                            if (permSessions && permSessions.length > 0) {
                                $.each(permSessions, function (i, session) {
                                    var sessionId = session.id;
                                    $F.log("Chat - perm session", "Load permanent session " + sessionId);
                                    var client = session.client;
                                    var newSession = createSession(sessionId, client, -1, '', {});
                                    newSession.permFlag = 1;
                                    newSession.sessionType = session.sessionType;
                                    sessionsMap["session" + sessionId] = newSession;
                                    sessionIdsArr[sessionIdsArr.length] = sessionId;
                                    lastCreatedSessionId = "session" + sessionId;
                                    setupClientDisplayName(client);
                                    setupClientIcon(client);
                                    createNewTab('sessionTab_' + sessionId, client);
                                    if (i === 0) {
                                        switchTabAndSession('sessionTab_' + sessionId);
                                    }
                                });
                                updatePermSessionsTimer = setTimeout(function () {
                                    timedUpdatePermSessions();
                                }, timeoutForUpdatePermSessions);
                                var enableRefreshClients = true;
                                $('.display_name .refreshClients', container).on('click', function () {
                                    var refreshBtn = $(this);
                                    if (enableRefreshClients) {
                                        enableRefreshClients = false;
                                        refreshBtn.css('cursor', 'not-allowed');
                                        setTimeout(function () {
                                            refreshBtn.css('cursor', 'pointer');
                                            enableRefreshClients = true;
                                        }, 10000);
                                        updatePermSessions(function () {
                                            $F.notify(i18n.refreshUserSuccess);
                                        });
                                    } else {
                                        $F.notify(i18n.error.cannotRefreshContinuously, 0);
                                    }
                                }).show();
                            } else {
                                //初始化活动会话，坐席意外退出，短时间内重连时候可以恢复之前的活动会话
                                if (c.configs.activeSessions) {
                                    $.each(c.configs.activeSessions, function (i, session) {
                                        var sessionId = session.sessionId;
                                        $F.log("Chat - recover session", "try to recover active session " + sessionId);
                                        var client = session.client;
                                        var queueId = session.queueId;
                                        var queueName = session.queueName;
                                        var sessionParams = session.sessionParams;
                                        var eventGuid = session.eventGuid;
                                        var historySessions = session.historySessions;
                                        var newSession = createSession(sessionId, client, queueId, queueName, sessionParams, eventGuid);
                                        newSession.languageTo = session.languageTo;
                                        newSession.sessionType = session.sessionType;
                                        sessionsMap["session" + sessionId] = newSession;
                                        sessionIdsArr[sessionIdsArr.length] = sessionId;
                                        lastCreatedSessionId = "session" + sessionId;
                                        // 允许音视频
                                        getPropertyByQueue('allowAudioVisual', queueId).then(msg => {
                                            if (msg) {
                                                $('.video-meeting-btn', container).click(function (e) {
                                                    e.stopPropagation();
                                                    if (videoSdkType === 'sanTi') {
                                                        $('.meeting', container).css("display", "inline-block");
                                                        $('.video-meeting-btn').css('display', 'none')
                                                    } else if (videoSdkType === 'juFeng') {
                                                        $('.jufengMeeting', container).css("display", "inline-block");
                                                        $('.video-meeting-btn').css('display', 'none')
                                                    }

                                                })
                                                if (msg.type && msg.type === 'tencent') {
                                                    require(['js/plugins/chat/chat.tencent.meeting'], function (Meeting) {
                                                        Meeting.loadTencentMeet(msg, sessionId, session.client.name, container);
                                                    });
                                                } else if (msg.type && msg.type === 'sanTi') {
                                                    $('.meeting .caller .video .forwardBox').click(function (event) {
                                                        $('.meeting', container).hide();
                                                        $('.video-meeting-btn').css("display", "inline-block");
                                                    });
                                                    videoSdkType = 'sanTi'
                                                    require(['js/plugins/chat/chat.sanTi.meeting'], function (Meeting) {
                                                        globalMeeting = Meeting;
                                                        $('.chat .chat_video', container).show();
                                                        $('.chat .chat_video', container).off('click')
                                                        $('.chat .chat_video').click(function (event) {
                                                            $('.meeting.videoMeet .waitAccept', container).show();
                                                            if (!isVideoCallIng) {
                                                                currentVideoSessionId = currentSessionId
                                                                sessionsMap["session" + currentSessionId].locked = true
                                                                sessionsMap["session" + currentSessionId].videoActive = true
                                                                Meeting.loadSanTiMeet(sendMessage, chatServerAddr, msg, currentSessionId, session.client.name, container);
                                                            } else {
                                                                $F.notify('正在通话中不允许点击！');
                                                            }
                                                        });
                                                        $('.meeting .acceptVCall', container).off('click')
                                                        //接受视频
                                                        $('.meeting .acceptVCall', container).click(function (e) {
                                                            e.stopPropagation();
                                                            if (videoNoResponseTimer) clearTimeout(videoNoResponseTimer)
                                                            var acceptSession = sessionsMap["session" + acceptVideoSessionId];
                                                            $('.meeting.videoMeet .audio', container).hide();
                                                            $('.meeting.videoMeet .video', container).show();
                                                            $('.meeting .video .titleBox .title').html('视频通话 ' + ` (${acceptSession.client.name})`)
                                                            currentVideoSessionId = acceptVideoSessionId
                                                            const sentInfoJSON = JSON.stringify({
                                                                sessionId: acceptVideoSessionId,
                                                                roomId: acceptRoomId,
                                                                actionType: "acceptRequest",
                                                                from: 2
                                                            });
                                                            callService({
                                                                data: {
                                                                    requestType: "videoMeetingEvent",
                                                                    data: sentInfoJSON
                                                                },
                                                                success: data => {
                                                                    acceptSession.locked = true
                                                                    acceptSession.videoActive = true
                                                                    Meeting.loadSanTiMeet(sendMessage, chatServerAddr, msg, acceptVideoSessionId, acceptSession.client.name, container, acceptRoomId);
                                                                    isVideoCallIng = true
                                                                }
                                                            }, true).fail(function (e) {
                                                                $F.err('acceptRequestError', e)
                                                            });
                                                        });
                                                        $('.meeting .rejectVCall', container).off('click')
                                                        //视频拒接c
                                                        $('.meeting .rejectVCall', container).click(function (e) {
                                                            e.stopPropagation();
                                                            if (videoNoResponseTimer) clearTimeout(videoNoResponseTimer)
                                                            let acceptSession = sessionsMap["session" + acceptVideoSessionId];
                                                            $('.meeting', container).hide()
                                                            const sentInfoJSON = JSON.stringify({
                                                                sessionId: acceptVideoSessionId,
                                                                roomId: acceptRoomId,
                                                                actionType: "refuseRequest",
                                                                from: 2,
                                                            });
                                                            callService({
                                                                data: {
                                                                    requestType: "videoMeetingEvent",
                                                                    data: sentInfoJSON
                                                                },
                                                                success: data => {
                                                                    acceptSession.locked = false
                                                                    acceptSession.videoActive = false
                                                                    sendMessage(`${i18n.videoMeetingRecord.Refuse} [videoMeet]`, acceptVideoSessionId)
                                                                }
                                                            }, true).fail(function (e) {
                                                                $F.err('refuseRequestError', e)
                                                            });

                                                        });
                                                        $('.meeting .releaseVCall', container).off('click')
                                                        //视频挂断
                                                        $('.meeting .releaseVCall', container).click(function (e) {
                                                            e.stopPropagation();
                                                            $('.video-meeting-btn').css("display", "none");
                                                            if (isVideoCallIng) { //挂断电话
                                                                $('#selfvideo', container).hide();
                                                                $('#othervideo', container).hide();
                                                                $('.meeting', container).hide();
                                                                Meeting.levelRoomRequest()
                                                                Meeting.leaveRoom()
                                                                if (sessionsMap["session" + currentVideoSessionId].locked) {
                                                                    sessionsMap["session" + currentVideoSessionId].lastAgentMessageSentTime = new Date().getTime();
                                                                }
                                                                sessionsMap["session" + currentVideoSessionId].locked = false
                                                                sessionsMap["session" + currentVideoSessionId].videoActive = false
                                                                sendMessage(`${i18n.videoMeetingRecord.Connect} ${formatTimestampDiff(new Date().getTime() - meetingStartTime)} [videoMeet]`, acceptVideoSessionId)
                                                            } else { //取消电话
                                                                $('#selfvideo', container).hide();
                                                                $('#othervideo', container).hide();
                                                                $('.meeting', container).hide();
                                                                Meeting.cancelRequest()
                                                                Meeting.leaveRoom()
                                                            }
                                                            isVideoCallIng = false
                                                            sessionsMap["session" + currentVideoSessionId].locked = false
                                                        });
                                                    });
                                                } else if (msg.type && msg.type === 'juFeng') {
                                                    $('.jufengMeeting .caller .video .forwardBox').click(function (event) {
                                                        $('.jufengMeeting', container).hide();
                                                        $('.video-meeting-btn').css("display", "inline-block");
                                                    });
                                                    videoSdkType = 'juFeng'
                                                    require(['js/plugins/chat/chat.jufeng.meeting'], function (Meeting) {
                                                        globalMeeting = Meeting;
                                                        $('.chat .chat_video', container).show();
                                                        $('.chat .chat_video').click(function (event) {
                                                            $('.jufengMeeting.videoMeet .waitAccept', container).show();
                                                            if (!isVideoCallIng) {
                                                                currentVideoSessionId = currentSessionId
                                                                sessionsMap["session" + currentSessionId].locked = true
                                                                sessionsMap["session" + currentSessionId].videoActive = true
                                                                Meeting.loadSanTiMeet(sendMessage, chatServerAddr, msg, currentSessionId, session.client.name, container);
                                                            } else {
                                                                $F.notify('正在通话中不允许点击！');
                                                            }
                                                        });
                                                        $('.jufengMeeting .acceptVCall', container).off('click')
                                                        //接受视频
                                                        $('.jufengMeeting .acceptVCall', container).click(function (e) {
                                                            e.stopPropagation();
                                                            meetingStartTime = new Date().getTime()
                                                            if (videoNoResponseTimer) clearTimeout(videoNoResponseTimer)
                                                            let acceptSession = sessionsMap["session" + acceptVideoSessionId];
                                                            $('.jufengMeeting.videoMeet .audio', container).hide();
                                                            $('.jufengMeeting.videoMeet .video', container).show();
                                                            $('.jufengMeeting .video .titleBox .title').html('视频通话 ' + ` (${acceptSession.client.name})`)
                                                            currentVideoSessionId = acceptVideoSessionId
                                                            const sentInfoJSON = JSON.stringify({
                                                                sessionId: acceptVideoSessionId,
                                                                roomId: acceptRoomId,
                                                                actionType: "acceptRequest",
                                                                from: 2
                                                            });
                                                            callService({
                                                                data: {
                                                                    requestType: "videoMeetingEvent",
                                                                    data: sentInfoJSON
                                                                },
                                                                success: data => {
                                                                    acceptSession.locked = true
                                                                    acceptSession.videoActive = true
                                                                    Meeting.loadSanTiMeet(sendMessage, chatServerAddr, msg, acceptVideoSessionId, acceptSession.client.name, container, acceptRoomId);
                                                                    isVideoCallIng = true
                                                                }
                                                            }, true).fail(function (e) {
                                                                $F.err('acceptRequestError', e)
                                                            });
                                                        });
                                                        $('.jufengMeeting .rejectVCall', container).off('click')
                                                        //视频拒接c
                                                        $('.jufengMeeting .rejectVCall', container).click(function (e) {
                                                            e.stopPropagation();
                                                            if (videoNoResponseTimer) clearTimeout(videoNoResponseTimer)
                                                            let acceptSession = sessionsMap["session" + acceptVideoSessionId];
                                                            $('.jufengMeeting', container).hide()
                                                            const sentInfoJSON = JSON.stringify({
                                                                sessionId: acceptVideoSessionId,
                                                                roomId: acceptRoomId,
                                                                actionType: "refuseRequest",
                                                                from: 2,
                                                            });
                                                            callService({
                                                                data: {
                                                                    requestType: "videoMeetingEvent",
                                                                    data: sentInfoJSON
                                                                },
                                                                success: data => {
                                                                    acceptSession.locked = false
                                                                    acceptSession.videoActive = false
                                                                    sendMessage(`${i18n.videoMeetingRecord.Refuse} [videoMeet]`, acceptVideoSessionId)
                                                                }
                                                            }, true).fail(function (e) {
                                                                $F.err('refuseRequestError', e)
                                                            });

                                                        });
                                                        $('.jufengMeeting .releaseVCall', container).off('click')
                                                        //视频挂断
                                                        $('.jufengMeeting .releaseVCall', container).click(function (e) {
                                                            e.stopPropagation();
                                                            $('.video-meeting-btn').css("display", "none");
                                                            if (isVideoCallIng) { //挂断电话
                                                                $('#selfvideo', container).hide();
                                                                $('#othervideo', container).hide();
                                                                $('.jufengMeeting', container).hide();
                                                                Meeting.levelRoomRequest()
                                                                // Meeting.leaveRoom()
                                                                if (sessionsMap["session" + currentVideoSessionId].locked) {
                                                                    sessionsMap["session" + currentVideoSessionId].lastAgentMessageSentTime = new Date().getTime();
                                                                }
                                                                sessionsMap["session" + currentVideoSessionId].locked = false
                                                                sessionsMap["session" + currentVideoSessionId].videoActive = false
                                                                sendMessage(`${i18n.videoMeetingRecord.Connect} ${formatTimestampDiff(new Date().getTime() - meetingStartTime)} [videoMeet]`, acceptVideoSessionId)
                                                            } else { //取消电话
                                                                $('#selfvideo', container).hide();
                                                                $('#othervideo', container).hide();
                                                                $('.jufengMeeting', container).hide();
                                                                Meeting.cancelRequest()
                                                                // Meeting.leaveRoom()
                                                            }
                                                            isVideoCallIng = false
                                                            sessionsMap["session" + currentVideoSessionId].locked = false
                                                        });
                                                    })
                                                }
                                            } else {
                                                $('.chat .chat_audio', container).hide();
                                                $('.chat .chat_video', container).hide();
                                            }
                                        });
                                        if (historySessions && historySessions.length > 0) {
                                            $.each(historySessions, function (i, hs) {
                                                var lastMsg;
                                                $.each(hs.newMessages, function (j, message) {
                                                    if (!Robot.isRobot(message.userId)) { //非机器人时候才需要parse
                                                        message.text = tools.parseDisplayContent(message.text);
                                                    } else {
                                                        message.text = Robot.parseRobotMessage(message.text, message.userId);
                                                    }
                                                    if (message.text) {
                                                        try {
                                                            message.reference = JSON.parse(message.reference)
                                                        } catch (e) { }
                                                        if (message.messageType === NOTICE_TYPE.CUSTOM) {
                                                            addMessageToSession(sessionId, {
                                                                id: "-1",
                                                                name: null
                                                            }, {
                                                                text: formatFeedbackMessage(message.text),
                                                                type: MESSAGE_TYPE.SYSTEM
                                                            });
                                                        } else {
                                                            addMessageToSession(sessionId, {
                                                                id: message.userId,
                                                                name: message.userName,
                                                                icon: message.icon
                                                            }, {
                                                                id: message.revokeId || message.id,
                                                                text: message.text,
                                                                revokeFlag: message.revokeFlag,
                                                                history: true,
                                                                translate: message.translateMessage,
                                                                quoteMessageText: (message.reference && message.reference.content) ? message.reference.content.displayText : undefined,
                                                                quoteMessageId: message.reference ? message.reference._id : undefined,
                                                                quoteMessageSender: (message.reference && message.reference.user) ? message.reference.user.name : ''
                                                            }, Tools.getFormattedTime(message.postTime));
                                                        }
                                                    }
                                                    if (message.messageType === 0) { // 只考虑普通消息，自动提示不算
                                                        lastMsg = message;
                                                    }
                                                });
                                                if (sessionId == hs.id) {
                                                    // 恢复的会话最后一条消息如果是客户的，则标识客户在等待中，避免没有标识造成错误的认为客户长时间不说话而自动结束聊天
                                                    if (lastMsg && lastMsg.userId === newSession.clientId) {
                                                        setUserInWaiting(newSession, true);
                                                        newSession.lastUserMessageReceivedTime = lastMsg.postTime;
                                                        newSession.lastAutoReplyMessageTime = newSession.lastUserMessageReceivedTime;
                                                    }
                                                    addMessageToSession(sessionId, {
                                                        id: "-1",
                                                        name: null
                                                    }, {
                                                        text: formatFeedbackMessage('继续之前会话'),
                                                        type: MESSAGE_TYPE.SYSTEM
                                                    }, undefined);
                                                } else {
                                                    var sessionEndTime = '';
                                                    if (hs.sessionInfo && hs.sessionInfo.endTime) {
                                                        sessionEndTime = ' [' + new Date(hs.sessionInfo.endTime).format('yyyy-MM-dd hh:mm:ss') + ']';
                                                    }
                                                    addMessageToSession(sessionId, {
                                                        id: "-1",
                                                        name: null
                                                    }, {
                                                        text: '<div class="notice"><span class="line">————————</span>会话' + hs.id + sessionEndTime + '<span class="line">————————</span></div>',
                                                        history: true,
                                                        type: MESSAGE_TYPE.SYSTEM
                                                    });
                                                }
                                            });
                                        }
                                        setupClientDisplayName(client);
                                        setupClientIcon(client);
                                        createNewTab('sessionTab_' + sessionId, client);
                                        switchTabAndSession('sessionTab_' + sessionId);
                                    });
                                }
                            }
                            c.configs.fileAcceptExtensions = c.configs.fileAcceptExtensions ? c.configs.fileAcceptExtensions.split(',') : [];

                            //注册划词搜索
                            if (c.configs.moveSearchCannedMessageEnabled) {
                                $('#agentResponseDiv', container).on('mouseup', function () {
                                    var selectionText = Tools.getSelectionText();
                                    if (selectionText) {
                                        notifyProjectEvent('MOVE_SEARCH', {
                                            sessionId: currentSessionId,
                                            text: selectionText
                                        });
                                    }
                                });
                            }

                            //是否允许坐席修改状态
                            if (c.configs.allowAgentStatusChange === 'false') {
                                $('#mmpop_system_menu .status-change', container).hide();
                            }

                            if (c.configs.agentRealtimeQueueMonitorEnable) {
                                $('.stat_info', container).show();
                                if ($('.stat_info') && $('.stat_info')[0]) {
                                    $('.stat_info')[0].addEventListener('click', function () {
                                        notifyProjectEvent($CONST.ChatEvent.CLICK_QUEUE_NUMBER);
                                    })
                                }
                            }
                        } else {
                            $F.err('Chat - getAgentConfigurations', 'Get configuration failed', data);
                        }
                        callback(null, "config");
                    }).fail(function (e) {
                        $F.err('Chat - getAgentConfigurations', 'Get configuration failed', e);
                        callback("get config ajax failed", "config");
                    });

                    //初始化userStatus下拉内容
                    callService({
                        data: {
                            requestType: "getUserStatus"
                        }
                    }).done(function (data) {
                        if (data && data.status === "success") {
                            $.each(data.userStatus, function (i, us) {
                                c.userStatus[us.userStatusId] = us;
                                // $('#statusDialog .chat-select', container).append('<option value=' + us.userStatusId + ' ' + (us.userStatusId == 1 ? 'selected' : '') + '>' + us.userStatus + '</option>');
                                if (!us.reservingFlag) {
                                    $('.chat .user_status_menu ul').append('<li data-status="' + us.userStatusId + '"><a href="javascript:;" title="' + us.userStatus + '">' + us.userStatus + (us.userStatusId === 1 ? '<i class="selected fa fa-check" />' : '') + '</a></li>');
                                }
                            });

                            $('.chat .user_status_menu ul>li', container).hover(function () {
                                clearTimeout(c.statusChangeHoverTimer);
                            }, function () {
                                c.statusChangeHoverTimer = setTimeout(function () {
                                    c.hideUserStatusMenu();
                                }, 1000);
                            });
                            $('.chat .user_status_menu ul>li', container).click(function () {
                                var userStatusId = parseInt($(this).attr('data-status'));
                                userStatusId = checkForReservingStatusWhenStatusChange(userStatusId);
                                if (c.currentUserStatus !== userStatusId) {
                                    if (chatExclusion) {
                                        //AgentState.EAS_READY : 2  如果当前电话是准备好状态
                                        if ($E.softphone && $E.softphone.currentAgentState == 2) {
                                            if (userStatusId === USER_STATUS.ONLINE) {
                                                return $F.alert(i18n.error.cannotReadyWhilePhoneHasReady);
                                            }
                                        }
                                    }
                                    c.changeStatus(userStatusId);
                                }
                            });

                            c.setUserStatus();
                        } else {
                            $F.err('Chat - getUserStatus', i18n.error.getUserStatusFailed, data);
                        }
                    }).fail(function () {
                        $F.err('Chat - getUserStatus', i18n.error.getUserStatusFailed);
                    });


                    if (dirtyWordsScanEnabled) {
                        //加载脏词库
                        callService({
                            data: {
                                requestType: 'loadDirtyWords'
                            }
                        }).done(function (data) {
                            if (data.status === "success") {
                                c.dirtyWords = data.dirtyWords;
                            } else {
                                $F.err('Chat - loadDirtyWords', i18n.error.loadDirtywordsFailed);
                            }
                        }).fail(function () {
                            $F.err('Chat - loadDirtyWords', i18n.error.loadDirtywordsFailed);
                        });
                    }
                    if (highlightWordsScanEnable) {
                        //加载关键词词库
                        callService({
                            data: {
                                requestType: 'loadHighlightWords'
                            }
                        }).done(function (data) {
                            if (data.status === "success") {
                                c.highlightWords = data.highlightWords;
                            } else {
                                $F.err('Chat - loadHighlightWords', i18n.error.loadHighlightWordsFailed);
                            }
                        }).fail(function () {
                            $F.err('Chat - loadHighlightWords', i18n.error.loadHighlightWordsFailed);
                        });
                    }
                    //加载禁用词词库
                    callService({
                        data: {
                            requestType: 'loadForbidWords'
                        }
                    }).done(function (data) {
                        if (data.status === "success") {
                            c.forbidWords = data.forbidWords;
                        } else {
                            $F.err('Chat - loadForbidWords', i18n.error.loadForbidWordsFailed);
                        }
                    }).fail(function () {
                        $F.err('Chat - loadForbidWords', i18n.error.loadForbidWordsFailed);
                    });


                    if (!disableSplitter) {
                        //创建聊天item列表与详情的左右splitter
                        $('.chat', container).wisSplitter({
                            direct: 'h',
                            cookie: 'chat.chatSplitter',
                            fixedSize: 211,
                            minSize: 190,
                            maxSize: 300
                        });

                        //创建聊天消息历史和输入框的上下splitter
                        $('.session_content_container', container).wisSplitter({
                            direct: 'v',
                            cookie: 'chat.contentSplitter',
                            minSize: 110,
                            maxSize: 400,
                            fixedSize: 180,
                            fixed: 1
                        });
                    }
                }
            ], function (err, results) {
                $F.busyHide(busyId);
                if (!err) {
                    $F.log('Chat - init', 'Init chat successful');

                    userWaitingTimer = setTimeout(function () {
                        userWaitingElapseTicker(c);
                    }, 1000);
                    c.worker.postMessage({
                        cmd: 'start',
                        params: {
                            url: chatServerAddr + '/' + tools.ajaxURL,
                            data: {
                                requestType: 'keep'
                            }
                        }
                    });

                    c.worker.addEventListener('message', e => {
                        if (e.data.action === 'pull') {
                            if (c.logined) {
                                pullFromServer(c);
                            }
                        }
                    }, false);

                    c.worker.postMessage({
                        cmd: 'doPull',
                        params: {
                            timeout: c.configs.timeoutForPull
                        }
                    });

                    //开始pull检查
                    startCheckPull();
                } else {
                    $F.err('Chat - init', 'Init chat failed: ' + err);
                }
            });
        };

        /**
         * iframe通信
         * @param data 传递给iframe中的数据
         * @param url  iframe地址
         * @param iframeID iframe id
         */
        function sendMessageIframe() {

        }

        //设置过了CKEDITOR.disableAutoInline = true; 就不需要单独先初始化CKEditor了
        H5Utils.loadPlugin({
            name: 'chat',
            container: container,
            html: 'chat/chat.' + ($E.lang === 'zh_CN' ? '' : $E.lang + '.') + 'html',
            css: 'chat/' + chatStyle + '.css',
            done: function (dom) {
                c.pluginDom = dom;
                init();
            }
        });

        return c;
    };

    Chat.prototype = {

        /**
         * 重新激活插件，刷新插件数据
         * @method active
         * @param parameter {any} 插件入参
         */
        active: function (parameter) {
            if (this.parameter == parameter)
                return;
            this.parameter = parameter;
        },

        close: function () {
            $F.log('Chat', i18n.closeChat);
            this.logoutAndClose();
        }
    };

    // 老ngs兼容處理
    function compatibleOldNgsFunctions(project) {
        if ($E.rightPanel instanceof Function) {
            $E.openChatPanel = $E.rightPanel;
        }
        if (project.rightPanelSwitch) {
            project.chatSwitch = project.rightPanelSwitch;
        }
    }

    return {
        avaliable: function (project, addin, addinParam, parameter) {
            return {
                code: 1,
                message: 'OK'
            }
        },

        close: function () {
            $F.log('Chat - close', 'Chat page closed ...');
        },

        newInstance: function (project, container, parameters) {
            return new Chat(project, container, parameters);
        },

        destroy: function () {

        }
    }
});